Primera Version

This commit is contained in:
Miguel 2025-04-03 09:05:09 +02:00
commit 4ee8015953
11 changed files with 1307 additions and 0 deletions

174
.gitignore vendored Normal file
View File

@ -0,0 +1,174 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc

22
chatgtp.py Normal file
View File

@ -0,0 +1,22 @@
import json
IP_HISTORY_FILE = "tu_ruta_a_ip_history.json" # Asegúrate de definir la ruta correcta
def remove_duplicates_preserve_order(history):
"""Elimina duplicados de la lista manteniendo el orden."""
seen = set()
new_history = []
for item in history:
if item not in seen:
seen.add(item)
new_history.append(item)
return new_history
if os.path.exists(IP_HISTORY_FILE):
with open(IP_HISTORY_FILE, "r") as file:
ip_history = json.load(file)
ip_history = remove_duplicates_preserve_order(ip_history)
# Guardar la lista actualizada en el archivo JSON
with open(IP_HISTORY_FILE, "w") as file:
json.dump(ip_history, file)

106
ip-changer-admin.py Normal file
View File

@ -0,0 +1,106 @@
import sys
import subprocess
import ctypes
import time
import logging
import os
from datetime import datetime
def setup_logging():
"""Configura el logging para el script"""
log_dir = os.path.join(os.path.dirname(__file__), 'logs')
if not os.path.exists(log_dir):
os.makedirs(log_dir)
log_file = os.path.join(log_dir, 'ip_changer_admin.log')
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file),
logging.StreamHandler(sys.stdout)
]
)
return logging.getLogger(__name__)
def is_admin():
"""Verifica si el script se está ejecutando con privilegios de administrador"""
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
def run_command(logger, cmd: str) -> bool:
"""Ejecuta un comando y retorna True si fue exitoso"""
logger.info(f"Executing command: {cmd}")
try:
result = subprocess.run(
cmd,
shell=True,
capture_output=True,
text=True
)
if result.stdout:
logger.info(f"Command output: {result.stdout}")
if result.stderr:
logger.error(f"Command error: {result.stderr}")
return result.returncode == 0
except Exception as e:
logger.error(f"Error executing command: {str(e)}")
return False
def main():
logger = setup_logging()
# Log startup information
logger.info("=" * 50)
logger.info(f"Script started at {datetime.now()}")
logger.info(f"Arguments received: {sys.argv}")
logger.info(f"Running with admin privileges: {is_admin()}")
if not is_admin():
logger.error("This script requires administrator privileges")
return 1
if len(sys.argv) < 3:
logger.error("Insufficient arguments")
logger.info("Usage: ip_changer_admin.py <interface> <ip|dhcp>")
logger.info("Example for static IP: ip_changer_admin.py Ethernet 192.168.1.100")
logger.info("Example for DHCP: ip_changer_admin.py Ethernet dhcp")
return 1
interface = sys.argv[1]
ip_mode = sys.argv[2]
logger.info(f"Processing request for interface: {interface}")
logger.info(f"IP mode: {ip_mode}")
if ip_mode.lower() == "dhcp":
cmd = f'netsh interface ip set address "{interface}" dhcp'
else:
# Asumimos que es una IP estática
ip = ip_mode
gateway = '.'.join(ip.split('.')[:3] + ['1'])
cmd = f'netsh interface ip set address "{interface}" static {ip} 255.255.255.0 {gateway}'
success = run_command(logger, cmd)
if success:
logger.info("Command executed successfully")
return 0
else:
logger.error("Command failed")
return 1
if __name__ == "__main__":
try:
exit_code = main()
sys.exit(exit_code)
except Exception as e:
logger = setup_logging()
logger.exception("Unhandled exception occurred")
sys.exit(1)

1
ip_config.json Normal file
View File

@ -0,0 +1 @@
{"last_interface": "Ethernet", "last_ip_prefix": "10.1.33"}

1
ip_history.json Normal file
View File

@ -0,0 +1 @@
["10.1.22", "169.254.38", "10.1.20", "10.146.76", "192.168.0", "10.101.8", "10.1.33", "10.1.92", "192.168.121", "192.168.1", "169.254.3", "10.202.4", "192.168.2", "10.1.30", "192.168.88"]

192
ipchange - copia.py Normal file
View File

@ -0,0 +1,192 @@
import ctypes
import subprocess
import sys
import re
import os
import json
import time
from win10toast import ToastNotifier
# Definir una constante para la ruta del directorio
DIR = "D:\\Proyectos\\Scripts\\IPChanger"
IP_HISTORY_FILE = os.path.join(DIR, "ip_history.json")
def is_admin():
"""Verifica si el script se está ejecutando con privilegios de administrador."""
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
def run_as_admin(argv=None, debug=False):
"""Ejecuta el script como administrador."""
shell32 = ctypes.windll.shell32
if argv is None and shell32.IsUserAnAdmin():
# Ya estamos ejecutando como administrador, no es necesario hacer nada
return True
if argv is None:
argv = sys.argv
if hasattr(sys, "_MEIPASS"):
# Soporte para PyInstaller
arguments = map(str, argv[1:])
else:
arguments = map(str, argv)
argument_line = " ".join(arguments)
executable = str(sys.executable)
if debug:
print(f"Command line: {executable} {argument_line}")
ret = shell32.ShellExecuteW(None, "runas", executable, argument_line, None, 1)
if int(ret) <= 32:
return False
return None
def set_static_ip(ip_address, gateway):
"""Establece una dirección IP estática para el adaptador Ethernet."""
try:
subprocess.run(
f'netsh interface ip set address "Ethernet" static {ip_address} 255.255.255.0 {gateway}',
shell=True,
)
return f"Dirección IP establecida en {ip_address}"
except Exception as e:
return f"Error al establecer la dirección IP: {e}"
def set_dhcp():
"""Configura el adaptador Ethernet para obtener la dirección IP automáticamente a través de DHCP."""
try:
subprocess.run('netsh interface ip set address "Ethernet" dhcp', shell=True)
return "Adaptador Ethernet configurado para DHCP"
except Exception as e:
return f"Error al configurar DHCP: {e}"
def save_current_ip():
"""Guarda la dirección IP actual del adaptador Ethernet en un archivo."""
try:
ip_config_output = subprocess.check_output("ipconfig", shell=True).decode(
"cp850"
)
current_ip = re.search(
"Adaptador de Ethernet Ethernet:[\s\S]*?Dirección IPv4. . . . . . . . . . . . . . : (\d+\.\d+\.\d+\.\d+)",
ip_config_output,
)
if current_ip:
update_ip_history(current_ip.group(1))
return f"Dirección IP actual guardada: {current_ip.group(1)}"
else:
return "No se encontró la dirección IP actual."
except Exception as e:
return f"Error al guardar la dirección IP actual: {e}"
def update_ip_history(current_ip):
"""Actualiza el historial de las últimas 10 IPs usadas, excluyendo 'auto'."""
if current_ip.lower() == "auto":
return
if os.path.exists(IP_HISTORY_FILE):
with open(IP_HISTORY_FILE, "r") as file:
ip_history = json.load(file)
else:
ip_history = []
ip_history.insert(0, current_ip)
ip_history = ip_history[:10]
with open("ip_history.json", "w") as file:
json.dump(ip_history, file)
def restore_last_ip():
"""Restaura la última dirección IP guardada para el adaptador Ethernet."""
if os.path.exists(IP_HISTORY_FILE):
with open(IP_HISTORY_FILE, "r") as file:
ip_history = json.load(file)
if ip_history:
last_ip = ip_history[0] # Obtener la última IP del historial
gateway = last_ip.rsplit(".", 1)[0] + ".1"
return set_static_ip(last_ip, gateway)
else:
return "No hay direcciones IP guardadas en el historial."
else:
return "No se encontró el archivo de historial de IPs."
def get_input_parameter():
"""Solicita al usuario un parámetro por línea de comandos."""
return input(
"Ingrese la dirección IP, 'auto' para DHCP, o 'last' para la última IP: "
)
def display_ip_history(ip_history):
"""Muestra el historial de las últimas 10 IPs y permite al usuario seleccionar una."""
if ip_history:
for i, ip in enumerate(ip_history):
print(f"{i}: {ip}")
choice = input(
"Seleccione una IP del historial (0-9), o presione Enter para continuar: "
)
if choice.isdigit() and 0 <= int(choice) < len(ip_history):
return ip_history[int(choice)]
return None
def main():
if not is_admin():
# Si no se está ejecutando como administrador, solicitar elevación
result = run_as_admin()
if result is True:
print("Ejecutando como administrador.")
elif result is False:
print("No se pudo obtener privilegios de administrador.")
return
else:
# El script se reinició con privilegios de administrador
return
if os.path.exists(IP_HISTORY_FILE):
with open(IP_HISTORY_FILE, "r") as file:
ip_history = json.load(file)
argument = sys.argv[1] if len(sys.argv) > 1 else None
if not argument:
chosen_ip = display_ip_history()
if chosen_ip:
argument = chosen_ip
else:
argument = get_input_parameter()
# Guardar la dirección IP actual solo si no es 'auto'
if argument.lower() != "auto":
message = save_current_ip()
if argument == "auto":
message = set_dhcp()
elif argument == "last":
message = restore_last_ip()
else:
ip_parts = argument.split(".")
if len(ip_parts) == 3:
argument += ".249"
gateway = ".".join(ip_parts[:3]) + ".1"
message = set_static_ip(argument, gateway)
update_ip_history(argument) # Actualizar el historial solo con IPs estáticas
# Aquí asumimos que 'message' contiene el mensaje que quieres mostrar
try:
toaster = ToastNotifier()
toaster.show_toast("Cambio de IP", message, duration=10)
except Exception as e:
print(
f"Se produjo un error al mostrar la notificación, pero la operación se completó con éxito: {e}"
)
if __name__ == "__main__":
main()

203
ipchange.py Normal file
View File

@ -0,0 +1,203 @@
import ctypes
import subprocess
import sys
import re
import os
import json
import time
from win10toast import ToastNotifier
import threading
# Definir una constante para la ruta del directorio
DIR = "D:\\Proyectos\\Scripts\\IPChanger"
IP_HISTORY_FILE = os.path.join(DIR, "ip_history.json")
def is_admin():
"""Verifica si el script se está ejecutando con privilegios de administrador."""
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
def run_as_admin(argv=None, debug=False):
"""Ejecuta el script como administrador."""
shell32 = ctypes.windll.shell32
if argv is None and shell32.IsUserAnAdmin():
# Ya estamos ejecutando como administrador, no es necesario hacer nada
return True
if argv is None:
argv = sys.argv
if hasattr(sys, "_MEIPASS"):
# Soporte para PyInstaller
arguments = map(str, argv[1:])
else:
arguments = map(str, argv)
argument_line = " ".join(arguments)
executable = str(sys.executable)
if debug:
print(f"Command line: {executable} {argument_line}")
ret = shell32.ShellExecuteW(None, "runas", executable, argument_line, None, 1)
if int(ret) <= 32:
return False
return None
def set_static_ip(ip_address, gateway):
"""Establece una dirección IP estática para el adaptador Ethernet."""
try:
subprocess.run(
f'netsh interface ip set address "Ethernet" static {ip_address} 255.255.255.0 {gateway}',
shell=True,
)
return f"Dirección IP establecida en {ip_address}"
except Exception as e:
return f"Error al establecer la dirección IP: {e}"
def set_dhcp():
"""Configura el adaptador Ethernet para obtener la dirección IP automáticamente a través de DHCP."""
try:
subprocess.run('netsh interface ip set address "Ethernet" dhcp', shell=True)
return "Adaptador Ethernet configurado para DHCP"
except Exception as e:
return f"Error al configurar DHCP: {e}"
def save_current_ip(ip_history):
"""Guarda la dirección IP actual del adaptador Ethernet en un archivo y detecta si es de DHCP."""
try:
ip_config_output = subprocess.check_output("ipconfig /all", shell=True).decode(
"cp850"
)
# Buscar la sección del adaptador de Ethernet y DHCP
ethernet_config = re.search(
"Adaptador de Ethernet Ethernet:[\s\S]*?(DHCP habilitado[\s\S]*?)\r\n\r\n",
ip_config_output,
)
current_ip = re.search(
"Dirección IPv4. . . . . . . . . . . . . . : (\d+\.\d+\.\d+\.\d+)",
ethernet_config.group(1) if ethernet_config else "",
)
dhcp_enabled = re.search(
"DHCP habilitado . . . . . . . . . . . . . : sí",
ethernet_config.group(1) if ethernet_config else "",
)
if current_ip and not dhcp_enabled:
ip_history.insert(0, current_ip.group(1))
ip_history = ip_history[:10]
return f"Dirección IP actual guardada: {current_ip.group(1)}"
elif dhcp_enabled:
return "La dirección IP actual es obtenida por DHCP."
else:
return "No se encontró la dirección IP actual."
except Exception as e:
return f"Error al obtener la configuración IP: {e}"
def restore_last_ip(ip_history):
"""Restaura la última dirección IP guardada para el adaptador Ethernet."""
if ip_history:
last_ip = ip_history[0] # Obtener la última IP del historial
gateway = last_ip.rsplit(".", 1)[0] + ".1"
return set_static_ip(last_ip, gateway)
else:
return "No hay direcciones IP guardadas en el historial."
def get_input_parameter():
"""Solicita al usuario un parámetro por línea de comandos."""
return input(
"Ingrese la dirección IP, 0-9 para los ultimas IP, 'a' o 'auto' para DHCP, o 'l' o 'last' para la última IP: "
)
def display_ip_history(ip_history):
"""Muestra el historial de las últimas 10 IPs en orden inverso (de la más antigua a la más reciente) y permite al usuario seleccionar una."""
if ip_history:
for i, ip in enumerate(reversed(ip_history)):
print(f"{len(ip_history) - 1 - i}: {ip}")
return None
def remove_duplicates_preserve_order(ip_history):
"""Elimina duplicados de la lista manteniendo el orden."""
seen = set()
new_history = []
for item in ip_history:
if item not in seen:
seen.add(item)
new_history.append(item)
return new_history
def show_notification(message, duration):
toaster = ToastNotifier()
toaster.show_toast("Cambio de IP", message, duration=duration)
def main():
if not is_admin():
# Si no se está ejecutando como administrador, solicitar elevación
result = run_as_admin()
if result is True:
print("Ejecutando como administrador.")
elif result is False:
print("No se pudo obtener privilegios de administrador.")
return
else:
# El script se reinició con privilegios de administrador
return
if os.path.exists(IP_HISTORY_FILE):
with open(IP_HISTORY_FILE, "r") as file:
ip_history = json.load(file)
ip_history = remove_duplicates_preserve_order(ip_history)
else:
ip_history = []
argument = sys.argv[1] if len(sys.argv) > 1 else None
if not argument:
chosen_ip = display_ip_history(ip_history)
argument = get_input_parameter()
if len(argument) == 0:
return
if ip_history:
if len(argument) == 1:
if argument.isdigit() and 0 <= int(argument) < len(ip_history):
argument = ip_history[int(argument)]
# Guardar la dirección IP actual solo si no es 'auto'
message = save_current_ip(ip_history)
print(message)
if argument == "auto" or argument == "a":
message = set_dhcp()
elif argument == "last" or argument == "l":
if ip_history:
message = restore_last_ip(ip_history)
print(message)
else:
ip_parts = argument.split(".")
if len(ip_parts) == 3:
argument += ".249"
gateway = ".".join(ip_parts[:3]) + ".1"
message = set_static_ip(argument, gateway)
print(message)
with open(IP_HISTORY_FILE, "w") as file:
json.dump(ip_history, file)
notification_thread = threading.Thread(target=show_notification, args=(message, 3))
notification_thread.start()
if __name__ == "__main__":
main()

1
ipchanger.bat Normal file
View File

@ -0,0 +1 @@
python ipchange.py

1
last_ip.txt Normal file
View File

@ -0,0 +1 @@
10.1.30.249

551
menu-ip-change.py Normal file
View File

@ -0,0 +1,551 @@
import tkinter as tk
from tkinter import ttk, messagebox
import json
import os
import subprocess
import re
import ctypes
import sys
import ipaddress
from typing import List, Optional
from dataclasses import dataclass
import threading
import time
try:
import wmi
WMI_AVAILABLE = True
except ImportError:
WMI_AVAILABLE = False
print("WMI module not found. Using alternative method for network interface detection.")
@dataclass
class NetworkInterface:
name: str
description: str
ip_address: str = ""
subnet_mask: str = ""
gateway: str = ""
dhcp_enabled: bool = True
active: bool = True
adapter_id: str = ""
class IPChangeConfig:
def __init__(self):
self.config_file = "ip_config.json"
self.history_file = "ip_history.json"
self.load_config()
def load_config(self):
try:
if os.path.exists(self.config_file):
with open(self.config_file, 'r') as f:
config = json.load(f)
self.last_interface = config.get('last_interface', '')
self.last_ip_prefix = config.get('last_ip_prefix', '')
else:
self.last_interface = ''
self.last_ip_prefix = ''
except Exception as e:
print(f"Error loading config: {e}")
self.last_interface = ''
self.last_ip_prefix = ''
def save_config(self):
try:
config = {
'last_interface': self.last_interface,
'last_ip_prefix': self.last_ip_prefix
}
with open(self.config_file, 'w') as f:
json.dump(config, f)
except Exception as e:
print(f"Error saving config: {e}")
class IPChangerApp:
def __init__(self, master):
self.master = master
self.master.title("Network Interface IP Configuration")
self.master.geometry("900x700")
# Inicializar configuración antes que nada
self.config = IPChangeConfig()
# Inicializar estructuras de datos
self.interfaces: List[NetworkInterface] = []
self.ip_history: List[str] = []
# Inicializar WMI si está disponible
if WMI_AVAILABLE:
self.wmi_client = wmi.WMI()
# Cargar datos guardados antes de crear widgets
self.load_ip_history()
# Crear la interfaz
self.create_widgets()
# Actualizar la lista de IPs en el combo
self.update_history_display()
# Refrescar interfaces
self.refresh_interfaces()
# Configurar pesos de la cuadrícula
self.master.grid_columnconfigure(0, weight=1)
self.master.grid_rowconfigure(1, weight=1) # El log expandible
def get_ip_prefix(self, ip: str) -> str:
"""Extrae los primeros 3 octetos de una dirección IP"""
parts = ip.split('.')
if len(parts) >= 3:
return '.'.join(parts[:3])
return ip
def create_widgets(self):
# Panel superior para controles
self.control_frame = ttk.Frame(self.master, padding="10")
self.control_frame.grid(row=0, column=0, sticky='new')
# Sección de interfaces
self.interfaces_frame = ttk.LabelFrame(self.control_frame, text="Network Interfaces", padding="5")
self.interfaces_frame.grid(row=0, column=0, sticky='ew', pady=(0,10))
ttk.Label(self.interfaces_frame, text="Select Interface:").grid(row=0, column=0, sticky='w', padx=5)
self.if_var = tk.StringVar()
self.if_combo = ttk.Combobox(self.interfaces_frame, textvariable=self.if_var, state='readonly', width=50)
self.if_combo.grid(row=0, column=1, sticky='ew', padx=5)
self.if_combo.bind('<<ComboboxSelected>>', self.on_interface_selected)
# Info de configuración actual
self.current_frame = ttk.LabelFrame(self.control_frame, text="Current Configuration", padding="5")
self.current_frame.grid(row=1, column=0, sticky='ew', pady=(0,10))
self.current_ip_var = tk.StringVar()
self.current_mask_var = tk.StringVar()
self.current_gateway_var = tk.StringVar()
self.dhcp_status_var = tk.StringVar()
ttk.Label(self.current_frame, text="Current IP:").grid(row=0, column=0, sticky='w', padx=5)
ttk.Label(self.current_frame, textvariable=self.current_ip_var).grid(row=0, column=1, sticky='w')
ttk.Label(self.current_frame, text="Subnet Mask:").grid(row=1, column=0, sticky='w', padx=5)
ttk.Label(self.current_frame, textvariable=self.current_mask_var).grid(row=1, column=1, sticky='w')
ttk.Label(self.current_frame, text="Gateway:").grid(row=2, column=0, sticky='w', padx=5)
ttk.Label(self.current_frame, textvariable=self.current_gateway_var).grid(row=2, column=1, sticky='w')
ttk.Label(self.current_frame, text="DHCP Status:").grid(row=3, column=0, sticky='w', padx=5)
ttk.Label(self.current_frame, textvariable=self.dhcp_status_var).grid(row=3, column=1, sticky='w')
# Sección de configuración IP
self.ip_frame = ttk.LabelFrame(self.control_frame, text="IP Configuration", padding="5")
self.ip_frame.grid(row=2, column=0, sticky='ew', pady=(0,10))
ttk.Label(self.ip_frame, text="IP Prefix (first 3 octets):").grid(row=0, column=0, sticky='w', padx=5)
self.ip_prefix = tk.StringVar(value=self.config.last_ip_prefix)
self.ip_entry = ttk.Entry(self.ip_frame, textvariable=self.ip_prefix, width=30)
self.ip_entry.grid(row=0, column=1, sticky='w', padx=5)
ttk.Label(self.ip_frame, text="Last Octet:").grid(row=0, column=2, sticky='w', padx=5)
self.last_octet = tk.StringVar(value="249")
self.last_octet_entry = ttk.Entry(self.ip_frame, textvariable=self.last_octet, width=5)
self.last_octet_entry.grid(row=0, column=3, sticky='w', padx=5)
# Sección de historial
self.history_frame = ttk.LabelFrame(self.control_frame, text="IP History", padding="5")
self.history_frame.grid(row=3, column=0, sticky='ew', pady=(0,10))
ttk.Label(self.history_frame, text="Previous IPs:").grid(row=0, column=0, sticky='w', padx=5)
self.history_var = tk.StringVar()
self.history_combo = ttk.Combobox(
self.history_frame,
textvariable=self.history_var,
state='readonly',
width=50
)
self.history_combo.grid(row=0, column=1, sticky='ew', padx=5)
self.history_combo.bind('<<ComboboxSelected>>', self.on_history_selected)
# Botones de acción
self.button_frame = ttk.Frame(self.control_frame)
self.button_frame.grid(row=4, column=0, sticky='ew', pady=(0,10))
ttk.Button(self.button_frame, text="Set Static IP", command=self.set_static_ip).grid(row=0, column=0, padx=5)
ttk.Button(self.button_frame, text="Enable DHCP", command=self.set_dhcp).grid(row=0, column=1, padx=5)
ttk.Button(self.button_frame, text="Refresh Interfaces", command=self.refresh_interfaces).grid(row=0, column=2, padx=5)
# Log en la parte inferior
self.log_frame = ttk.LabelFrame(self.master, text="Operation Log", padding="5")
self.log_frame.grid(row=1, column=0, sticky='nsew', padx=10, pady=(0,10))
self.log_text = tk.Text(self.log_frame, wrap="none", height=10)
self.log_scrollbar_y = ttk.Scrollbar(self.log_frame, orient="vertical", command=self.log_text.yview)
self.log_scrollbar_x = ttk.Scrollbar(self.log_frame, orient="horizontal", command=self.log_text.xview)
self.log_text.configure(
yscrollcommand=self.log_scrollbar_y.set,
xscrollcommand=self.log_scrollbar_x.set
)
self.log_text.grid(row=0, column=0, sticky='nsew')
self.log_scrollbar_y.grid(row=0, column=1, sticky='ns')
self.log_scrollbar_x.grid(row=1, column=0, sticky='ew')
ttk.Button(self.log_frame, text="Clear Log", command=self.clear_log).grid(row=2, column=0, columnspan=2, pady=5)
self.log_frame.grid_columnconfigure(0, weight=1)
self.log_frame.grid_rowconfigure(0, weight=1)
def clear_log(self):
self.log_text.delete('1.0', tk.END)
def log_message(self, message: str):
self.log_text.insert(tk.END, f"{message}\n")
self.log_text.see(tk.END)
self.log_text.update_idletasks()
def load_ip_history(self):
try:
if os.path.exists(self.config.history_file):
with open(self.config.history_file, 'r') as f:
self.ip_history = json.load(f)
else:
self.ip_history = []
except Exception as e:
self.log_message(f"Error loading IP history: {e}")
self.ip_history = []
def save_ip_history(self):
try:
with open(self.config.history_file, 'w') as f:
json.dump(self.ip_history, f)
except Exception as e:
self.log_message(f"Error saving IP history: {e}")
def add_to_history(self, ip_prefix: str):
if ip_prefix and ip_prefix not in self.ip_history:
self.ip_history.insert(0, ip_prefix)
self.ip_history = self.ip_history[:15] # Mantener solo las últimas 15 IPs
self.save_ip_history()
self.update_history_display()
def update_history_display(self):
self.history_combo['values'] = self.ip_history
if self.ip_history:
self.history_combo.set(self.ip_history[0])
def on_history_selected(self, event):
selected_ip = self.history_var.get()
if selected_ip:
# Ensure we only get the first three octets
ip_parts = selected_ip.split('.')
if len(ip_parts) >= 3:
ip_prefix = '.'.join(ip_parts[:3])
self.ip_prefix.set(ip_prefix)
self.config.last_ip_prefix = ip_prefix
self.config.save_config()
self.log_message(f"Selected IP prefix from history: {ip_prefix}")
def refresh_interfaces(self):
self.interfaces.clear()
try:
if WMI_AVAILABLE:
self._refresh_interfaces_wmi()
else:
self._refresh_interfaces_netsh()
# Actualizar combobox
self.if_combo['values'] = [inf.name for inf in self.interfaces if inf.active]
# Seleccionar última interfaz usada si está disponible
if self.config.last_interface in self.if_combo['values']:
self.if_combo.set(self.config.last_interface)
self.on_interface_selected()
self.log_message("Interfaces refreshed successfully")
except Exception as e:
self.log_message(f"Error refreshing interfaces: {str(e)}")
self.show_error(f"Error refreshing interfaces: {str(e)}")
def _refresh_interfaces_wmi(self):
try:
adapters = self.wmi_client.Win32_NetworkAdapter(PhysicalAdapter=True)
configs = self.wmi_client.Win32_NetworkAdapterConfiguration()
for adapter in adapters:
config = next((cfg for cfg in configs if cfg.InterfaceIndex == adapter.InterfaceIndex), None)
if config and config.IPEnabled:
interface = NetworkInterface(
name=adapter.NetConnectionID,
description=adapter.Description,
ip_address=config.IPAddress[0] if config.IPAddress else "",
subnet_mask=config.IPSubnet[0] if config.IPSubnet else "",
gateway=config.DefaultIPGateway[0] if config.DefaultIPGateway else "",
dhcp_enabled=config.DHCPEnabled,
active=adapter.NetEnabled,
adapter_id=adapter.GUID
)
self.interfaces.append(interface)
except Exception as e:
raise Exception(f"WMI refresh error: {str(e)}")
def _refresh_interfaces_netsh(self):
try:
output = subprocess.check_output("netsh interface ipv4 show config", shell=True, text=True)
configs = output.split("\n\n")
for config in configs:
if not config.strip():
continue
name_match = re.search(r"Configuración de\s+\"(.+?)\"", config)
if not name_match:
continue
name = name_match.group(1)
ip_match = re.search(r"Dirección IP:\s+(\d+\.\d+\.\d+\.\d+)", config)
mask_match = re.search(r"Máscara de subred:\s+(\d+\.\d+\.\d+\.\d+)", config)
gateway_match = re.search(r"Puerta de enlace predeterminada:\s+(\d+\.\d+\.\d+\.\d+)", config)
dhcp_match = re.search(r"DHCP habilitado:\s+(\w+)", config)
interface = NetworkInterface(
name=name,
description=name,
ip_address=ip_match.group(1) if ip_match else "",
subnet_mask=mask_match.group(1) if mask_match else "",
gateway=gateway_match.group(1) if gateway_match else "",
dhcp_enabled=dhcp_match.group(1).lower() == "" if dhcp_match else True,
active=True
)
self.interfaces.append(interface)
except subprocess.CalledProcessError as e:
raise Exception(f"Error executing netsh: {str(e)}")
except Exception as e:
raise Exception(f"Error parsing netsh output: {str(e)}")
def on_interface_selected(self, event=None):
selected = self.if_var.get()
interface = next((inf for inf in self.interfaces if inf.name == selected), None)
if interface:
self.current_ip_var.set(interface.ip_address)
self.current_mask_var.set(interface.subnet_mask)
self.current_gateway_var.set(interface.gateway)
self.dhcp_status_var.set("Enabled" if interface.dhcp_enabled else "Disabled")
# Actualizar IP prefix si hay una IP válida
if interface.ip_address:
prefix = '.'.join(interface.ip_address.split('.')[:3])
self.ip_prefix.set(prefix)
# Guardar interfaz seleccionada
self.config.last_interface = selected
self.config.save_config()
self.log_message(f"Selected interface: {selected}")
def validate_ip_input(self) -> tuple[bool, str]:
try:
prefix = self.ip_prefix.get().strip()
last_octet = self.last_octet.get().strip()
# Validar formato del prefijo
if not re.match(r'^\d{1,3}\.\d{1,3}\.\d{1,3}', prefix):
return False, "IP prefix must be in format: xxx.xxx.xxx"
# Validar último octeto
if not last_octet.isdigit() or not 0 <= int(last_octet) <= 255:
return False, "Last octet must be between 0 and 255"
# Construir y validar IP completa
ip = f"{prefix}.{last_octet}"
ipaddress.IPv4Address(ip)
return True, ip
except ValueError as e:
return False, f"Invalid IP address: {str(e)}"
def execute_admin_script(self, interface: str, mode: str, debug: bool = True) -> bool:
"""
Ejecuta el script administrativo con los argumentos proporcionados
mode puede ser una IP o 'dhcp'
"""
# Obtener rutas absolutas
current_dir = os.path.dirname(os.path.abspath(__file__))
script_path = os.path.join(current_dir, "ip-changer-admin.py")
python_exe = sys.executable
# Verificar que el script existe
if not os.path.exists(script_path):
error_msg = f"Admin script not found at: {script_path}"
self.log_message(error_msg)
return False
# Construir la línea de argumentos
args = f'"{script_path}" "{interface}" "{mode}"'
if debug:
self.log_message("Debug Information:")
self.log_message(f"Current Directory: {current_dir}")
self.log_message(f"Script Path: {script_path}")
self.log_message(f"Python Executable: {python_exe}")
self.log_message(f"Full Command: {python_exe} {args}")
try:
self.log_message(f"Attempting to execute admin script with elevated privileges")
ret = ctypes.windll.shell32.ShellExecuteW(
None, # hwnd
"runas", # operation
python_exe, # file
args, # parameters
current_dir, # directory
1 # show command
)
result_code = int(ret)
if result_code > 32:
self.log_message(f"ShellExecuteW successful (return code: {result_code})")
return True
else:
error_codes = {
0: "The operating system is out of memory or resources",
2: "The specified file was not found",
3: "The specified path was not found",
5: "Access denied",
8: "Insufficient memory to complete the operation",
11: "Bad format",
26: "A sharing violation occurred",
27: "The file name association is incomplete or invalid",
28: "The DDE operation timed out",
29: "The DDE operation failed",
30: "The DDE operation is busy",
31: "The file name association is unavailable",
32: "No application is associated with the file"
}
self.log_message(f"Failed to execute admin script. Error code {result_code}: {error_msg}")
return False
except Exception as e:
self.log_message(f"Exception while executing admin script: {str(e)}")
return False
def set_static_ip(self):
interface = self.if_var.get()
if not interface:
self.show_error("Please select a network interface")
return
# Validar IP
is_valid, result = self.validate_ip_input()
if not is_valid:
self.show_error(result)
return
ip = result
# Guardar IP actual en el historial
self.add_to_history(self.ip_prefix.get())
# Ejecutar script con privilegios
if self.execute_admin_script(interface, ip, debug=True):
time.sleep(2) # Esperar a que se apliquen los cambios
self.refresh_interfaces()
self.log_message(f"Successfully set static IP {ip} on {interface}")
else:
self.show_error("Failed to set static IP. Check the log for details.")
def set_dhcp(self):
interface = self.if_var.get()
if not interface:
self.show_error("Please select a network interface")
return
# Ejecutar script con privilegios
if self.execute_admin_script(interface, "dhcp", debug=True):
time.sleep(2) # Esperar a que se apliquen los cambios
self.refresh_interfaces()
self.log_message(f"Successfully enabled DHCP on {interface}")
else:
self.show_error("Failed to enable DHCP. Check the log for details.")
def run_command(self, cmd: str) -> bool:
self.log_message(f"Executing command: {cmd}")
try:
process = subprocess.Popen(
cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True
)
while True:
output = process.stdout.readline()
if output == '' and process.poll() is not None:
break
if output:
self.log_message(output.strip())
retcode = process.poll()
error_output = process.stderr.read()
if error_output:
self.log_message(f"Error output:\n{error_output}")
if retcode == 0:
self.log_message("Command executed successfully")
return True
else:
self.log_message(f"Command failed with return code: {retcode}")
return False
except Exception as e:
self.log_message(f"Error executing command: {str(e)}")
return False
def is_admin(self):
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except Exception as e:
self.log_message(f"Error checking admin privileges: {str(e)}")
return False
def elevate_privileges(self):
if sys.platform == 'win32':
self.show_info("The application needs administrator privileges to change network settings. "
"Please confirm the UAC prompt.")
script_path = os.path.abspath(sys.argv[0])
try:
ctypes.windll.shell32.ShellExecuteW(
None,
"runas",
sys.executable,
f'"{script_path}"',
None,
1
)
self.master.quit()
except Exception as e:
self.log_message(f"Error elevating privileges: {str(e)}")
self.show_error("Failed to elevate privileges")
def show_error(self, message: str):
self.log_message(f"ERROR: {message}")
messagebox.showerror("Error", message)
def show_info(self, message: str):
self.log_message(f"INFO: {message}")
messagebox.showinfo("Information", message)
def main():
root = tk.Tk()
app = IPChangerApp(root)
root.mainloop()
if __name__ == "__main__":
main()

55
privilegios.py Normal file
View File

@ -0,0 +1,55 @@
import ctypes
import sys
import os
import subprocess
import re
def is_admin():
"""Verifica si el script se está ejecutando con privilegios de administrador."""
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
def run_as_admin(argv=None, debug=False):
"""Ejecuta el script como administrador."""
shell32 = ctypes.windll.shell32
if argv is None and shell32.IsUserAnAdmin():
# Ya estamos ejecutando como administrador, no es necesario hacer nada
return True
if argv is None:
argv = sys.argv
if hasattr(sys, '_MEIPASS'):
# Soporte para PyInstaller
arguments = map(str, argv[1:])
else:
arguments = map(str, argv)
argument_line = u' '.join(arguments)
executable = str(sys.executable)
if debug:
print(f'Command line: {executable} {argument_line}')
ret = shell32.ShellExecuteW(None, "runas", executable, argument_line, None, 1)
if int(ret) <= 32:
return False
return None
def main():
if not is_admin():
# Si no se está ejecutando como administrador, solicitar elevación
result = run_as_admin()
if result is True:
print("Ejecutando como administrador.")
elif result is False:
print("No se pudo obtener privilegios de administrador.")
return
else:
# El script se reinició con privilegios de administrador
return
# Aquí va el resto del código de tu script...
# Por ejemplo:
# save_current_ip()
# ...
if __name__ == "__main__":
main()