-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Ana Martinez Gomez <[email protected]>
- Loading branch information
Showing
4 changed files
with
289 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd"> | ||
<metadata> | ||
<id>internet_detector.vm</id> | ||
<version>1.0.0</version> | ||
<authors>Elliot Chernofsky and Ana Martinez Gomez</authors> | ||
<description>Tool to detect if internet connectivity exists</description> | ||
<dependencies> | ||
<dependency id="common.vm" version="0.0.0.20240821" /> | ||
<dependency id="python3" version="0.0.0.20231019" /> | ||
</dependencies> | ||
</metadata> | ||
</package> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
$ErrorActionPreference = 'Stop' | ||
Import-Module vm.common -Force -DisableNameChecking | ||
|
||
$toolName = 'internet_detector' | ||
$category = 'Utilities' | ||
|
||
# Install dependency for windows api | ||
VM-Pip-Install "pywin32" | ||
|
||
$toolDir = "${Env:RAW_TOOLS_DIR}\$toolName" | ||
if (-not (Test-Path $toolDir)) { # Check if the directory doesn't exist | ||
New-Item -Path $toolDir -ItemType Directory -Force | ||
} | ||
|
||
# Download the script | ||
(New-Object net.webclient).DownloadFile('https://raw.githubusercontent.com/mandiant/VM-Packages/main/packages/internet_detector.vm/tools/internet_detector.pyw', $toolDir) | ||
|
||
$executablePath = (Get-Command pythonw).Source | ||
$filePath = Join-Path $toolDir "$toolName.pyw" | ||
|
||
VM-Install-Shortcut $toolName $category $executablePath -arguments $filePath | ||
|
||
# Define the action to be performed | ||
$action = New-ScheduledTaskAction -Execute $executablePath -Argument "$filePath" | ||
|
||
# Define the trigger to run every 2 minutes | ||
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 2) | ||
|
||
# Create the scheduled task | ||
Register-ScheduledTask -Action $action -Trigger $trigger -TaskName 'Internet Detector' -Force |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
$ErrorActionPreference = 'Continue' | ||
Import-Module vm.common -Force -DisableNameChecking | ||
|
||
$toolName = 'internet_detector' | ||
$category = 'Utilities' | ||
|
||
VM-Uninstall $toolName $category |
239 changes: 239 additions & 0 deletions
239
packages/internet_detector.vm/tools/internet_detector.pyw
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
import threading | ||
import requests | ||
import win32api | ||
import win32gui | ||
import win32con | ||
import urllib3 | ||
import hashlib | ||
import winreg | ||
import signal | ||
import ctypes | ||
import time | ||
import os | ||
import re | ||
|
||
|
||
# Define constants | ||
CHECK_INTERVAL = 2 # Seconds | ||
CONNECT_TEST_URL_AND_RESPONSES = { | ||
"https://www.msftconnecttest.com/connecttest.txt": "Microsoft Connect Test", # HTTPS Test #1 | ||
"http://www.google.com": 'Google', # HTTP Test | ||
"https://www.wikipedia.com": 'Wikipedia', # HTTPS Test #2 | ||
"https://www.youtube.com": 'YouTube' # HTTPS Test #3 | ||
} | ||
SPI_SETDESKWALLPAPER = 20 | ||
SPIF_UPDATEINIFILE = 0x01 | ||
SPIF_SENDWININICHANGE = 0x02 | ||
COLOR_DESKTOP = 1 | ||
ICON_INDICATOR_ON = os.path.join(os.environ.get('VM_COMMON_DIR'), "indicator_on.ico") | ||
ICON_INDICATOR_OFF = os.path.join(os.environ.get('VM_COMMON_DIR'), "indicator_off.ico") | ||
DEFAULT_BACKGROUND = os.path.join(os.environ.get('VM_COMMON_DIR'), "background.png") | ||
INTERNET_BACKGROUND = os.path.join(os.environ.get('VM_COMMON_DIR'),"background-internet.png") | ||
|
||
# Global variables | ||
tray_icon = None | ||
stop_event = threading.Event() # To signal the background thread to stop | ||
hwnd = None # We'll assign the window handle here later | ||
check_thread = None | ||
tray_icon_thread = None | ||
# Win32 API icon handles | ||
hicon_indicator_off = None | ||
hicon_indicator_on = None | ||
|
||
def signal_handler(sig, frame): | ||
global check_thread, tray_icon_thread, tray_icon | ||
print("Ctrl+C detected. Exiting...") | ||
stop_event.set() # Signal the background thread to stop | ||
if check_thread: | ||
check_thread.join() | ||
if tray_icon_thread: | ||
tray_icon_thread.join() | ||
if tray_icon: | ||
del tray_icon | ||
exit(0) | ||
|
||
def load_icon(icon_path): | ||
try: | ||
return win32gui.LoadImage(None, icon_path, win32con.IMAGE_ICON, 0, 0, win32con.LR_LOADFROMFILE) | ||
except Exception as e: | ||
print(f"Error loading indicator icon: {e}") | ||
return None | ||
|
||
class SysTrayIcon: | ||
def __init__(self, hwnd, icon, tooltip): | ||
self.hwnd = hwnd | ||
self.icon = icon | ||
self.tooltip = tooltip | ||
# System tray icon data structure | ||
self.nid = (self.hwnd, 0, win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, | ||
win32con.WM_USER + 20, self.icon, self.tooltip) | ||
# Add the icon to the system tray | ||
win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, self.nid) | ||
|
||
def set_tooltip(self, new_tooltip): | ||
self.tooltip = new_tooltip | ||
self.nid = (self.hwnd, 0, win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, | ||
win32con.WM_USER + 20, self.icon, self.tooltip) | ||
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, self.nid) | ||
|
||
def set_icon(self, icon): | ||
self.icon = icon | ||
self.nid = (self.hwnd, 0, win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, | ||
win32con.WM_USER + 20, self.icon, self.tooltip) | ||
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, self.nid) | ||
|
||
def __del__(self): | ||
# Remove the icon from the system tray when the object is destroyed | ||
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, self.nid) | ||
|
||
# Attempt to extract a known good value in response. | ||
def extract_title(data): | ||
match = re.search(r'<title>(.*?)</title', data) | ||
if match: | ||
return match.group(1) | ||
else: | ||
return None | ||
|
||
def check_internet(): | ||
for url, expected_response in CONNECT_TEST_URL_AND_RESPONSES.items(): | ||
try: | ||
# Perform internet connectivity tests | ||
response = requests.get(url, timeout=5, verify=False) | ||
if expected_response in (extract_title(response.text) or response.text): | ||
print("Internet connectivity detected via URL: {}".format(url)) | ||
return True | ||
except: | ||
pass | ||
return False | ||
|
||
def check_internet_and_update_tray_icon(): | ||
global tray_icon, hicon_indicator_off, hicon_indicator_on | ||
if check_internet(): | ||
tray_icon.set_icon(hicon_indicator_on) | ||
tray_icon.set_tooltip("Internet Connection: Detected") | ||
# Set the background to internet connection background | ||
if get_wallpaper_path() != INTERNET_BACKGROUND: # Checked so program isn't continuously setting the wallpaper | ||
set_wallpaper(INTERNET_BACKGROUND) | ||
else: | ||
tray_icon.set_icon(hicon_indicator_off) | ||
tray_icon.set_tooltip("Internet Connection: Not Detected") | ||
# Reset background when internet is not detected | ||
if get_wallpaper_path() != DEFAULT_BACKGROUND: # Checked so program isn't continuously setting the wallpaper | ||
set_wallpaper(DEFAULT_BACKGROUND) | ||
|
||
def check_internet_loop(): | ||
while not stop_event.is_set(): | ||
check_internet_and_update_tray_icon() | ||
time.sleep(CHECK_INTERVAL) | ||
|
||
def tray_icon_loop(): | ||
global hwnd, tray_icon, hicon_indicator_off, hicon_indicator_on, stop_event | ||
# Load icons | ||
hicon_indicator_off = load_icon(ICON_INDICATOR_OFF) | ||
hicon_indicator_on = load_icon(ICON_INDICATOR_ON) | ||
|
||
# Wait for hwnd to be initialized | ||
while hwnd is None: | ||
time.sleep(0.1) | ||
|
||
if hicon_indicator_off is None or hicon_indicator_on is None: | ||
print("Error: Failed to load icons. Exiting TrayIconThread.") | ||
return | ||
|
||
tray_icon = SysTrayIcon(hwnd, hicon_indicator_off, "Internet Detector") | ||
|
||
while not stop_event.is_set(): | ||
msg = win32gui.PeekMessage(hwnd, 0, 0, 0) | ||
if msg and len(msg) == 6: | ||
win32gui.TranslateMessage(msg) | ||
win32gui.DispatchMessage(msg) | ||
time.sleep(0.1) | ||
|
||
def enable_transparency_effects(): | ||
try: | ||
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, | ||
r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", | ||
0, winreg.KEY_ALL_ACCESS) | ||
|
||
winreg.SetValueEx(key, "EnableTransparency", 0, winreg.REG_DWORD, 1) | ||
winreg.CloseKey(key) | ||
except WindowsError as e: | ||
print(f"Error accessing or modifying registry: {e}") | ||
|
||
def get_wallpaper_path(): | ||
"""Attempts to retrieve the path to the current wallpaper image.""" | ||
# Try to get the path from the registry (for wallpapers set through Windows settings) | ||
try: | ||
key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Control Panel\Desktop", 0, winreg.KEY_READ) | ||
value, _ = winreg.QueryValueEx(key, "Wallpaper") | ||
winreg.CloseKey(key) | ||
if value: | ||
return value | ||
except WindowsError: | ||
pass | ||
|
||
# Check for cached wallpaper files (if the above fails) | ||
cached_files_dir = os.path.join(os.getenv("APPDATA"), r"Microsoft\Windows\Themes\CachedFiles") | ||
transcoded_wallpaper_path = os.path.join(os.getenv("APPDATA"), r"Microsoft\Windows\Themes\TranscodedWallpaper") | ||
|
||
for file in os.listdir(cached_files_dir): | ||
if file.endswith(('.jpg', '.jpeg', '.bmp', '.png')): | ||
return os.path.join(cached_files_dir, file) | ||
|
||
if os.path.exists(transcoded_wallpaper_path): | ||
return transcoded_wallpaper_path | ||
|
||
# If all else fails, return None | ||
return None | ||
|
||
def set_wallpaper(image_path): | ||
"""Sets the desktop wallpaper to the image at the specified path.""" | ||
print("Setting wallpaper to: {}".format(image_path)) | ||
result = ctypes.windll.user32.SystemParametersInfoW( | ||
SPI_SETDESKWALLPAPER, 0, image_path, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE | ||
) | ||
if not result: | ||
print("Error setting wallpaper. Make sure the image path is correct.") | ||
|
||
def main_loop(): | ||
global stop_event, check_thread, tray_icon_thread, tray_icon | ||
# Create and start the threads | ||
tray_icon_thread = threading.Thread(target=tray_icon_loop) | ||
check_thread = threading.Thread(target=check_internet_loop) | ||
|
||
tray_icon_thread.start() | ||
# Wait for the tray icon to finish initializing | ||
while tray_icon is None: | ||
time.sleep(0.1) | ||
|
||
check_thread.start() | ||
|
||
while not stop_event.is_set(): | ||
time.sleep(1) | ||
|
||
|
||
if __name__ == "__main__": | ||
signal.signal(signal.SIGINT, signal_handler) | ||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) | ||
enable_transparency_effects() | ||
|
||
# Create a hidden window to receive messages (required for system tray icons) | ||
def wndProc(hwnd, msg, wparam, lparam): | ||
if lparam == win32con.WM_LBUTTONDBLCLK: | ||
print("Left button double clicked") | ||
elif msg == win32con.WM_COMMAND: | ||
if wparam == 1023: # Example menu item ID | ||
print("Exit selected") | ||
win32gui.DestroyWindow(hwnd) | ||
return win32gui.DefWindowProc(hwnd, msg, wparam, lparam) | ||
|
||
wc = win32gui.WNDCLASS() | ||
hinst = wc.hInstance = win32api.GetModuleHandle(None) | ||
wc.lpszClassName = "Internet Detector" | ||
wc.lpfnWndProc = wndProc | ||
classAtom = win32gui.RegisterClass(wc) | ||
hwnd = win32gui.CreateWindow(classAtom, "Internet Detector", 0, 0, 0, 0, 0, 0, 0, hinst, None) | ||
|
||
print("Current wallpaper: {}".format(get_wallpaper_path())) | ||
|
||
main_loop() |