Skip to content

Instantly share code, notes, and snippets.

@valinet
Last active December 20, 2020 22:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save valinet/e59222e23a603bab03f7d45de0dd2098 to your computer and use it in GitHub Desktop.
Save valinet/e59222e23a603bab03f7d45de0dd2098 to your computer and use it in GitHub Desktop.
Set Thunderbird as the foreground application in Windows, no matter what
# Set Thunderbird as the foreground application in Windows, no matter what
# Copyright (c) 2020 Valentin-Gabriel Radu
#
# MIT License
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from threading import Thread
from threading import Event
import sys
from ctypes import *
from win32com.client import GetObject
import win32api
import win32gui
import win32con
import win32process
# declarations below from https://github.com/thezdi/scripts/blob/master/python_injector.py
import ctypes
from ctypes.wintypes import BOOL
from ctypes.wintypes import DWORD
from ctypes.wintypes import HANDLE
from ctypes.wintypes import LPVOID
from ctypes.wintypes import LPCVOID
LPCSTR = LPCTSTR = ctypes.c_char_p
LPDWORD = PDWORD = ctypes.POINTER(DWORD)
class _SECURITY_ATTRIBUTES(ctypes.Structure):
_fields_ = [('nLength', DWORD),
('lpSecurityDescriptor', LPVOID),
('bInheritHandle', BOOL),]
SECURITY_ATTRIBUTES = _SECURITY_ATTRIBUTES
LPSECURITY_ATTRIBUTES = ctypes.POINTER(_SECURITY_ATTRIBUTES)
LPTHREAD_START_ROUTINE = LPVOID
kernel32 = windll.kernel32
kernel32.GetModuleHandleA.argtypes = [LPCSTR]
kernel32.GetModuleHandleA.restype = LPVOID
kernel32.GetProcAddress.argtypes = [LPVOID, LPCSTR]
kernel32.GetProcAddress.restype = LPVOID
kernel32.CreateRemoteThread.restype = HANDLE
kernel32.CreateRemoteThread.argtypes = (HANDLE,
LPSECURITY_ATTRIBUTES,
DWORD,
LPTHREAD_START_ROUTINE,
LPVOID,
DWORD,
LPDWORD)
# adapted example from https://stackoverflow.com/questions/16770909/python-win32gui-setforegroundwindow
# we also need to check if the window we got belongs to thunderbird.exe
def EnumWindowProc(hWnd, oarhWnd):
if win32gui.IsWindowVisible(hWnd):
text = win32gui.GetWindowText(hWnd)
if text != '':
if text.endswith(" - Mozilla Thunderbird"):
tid, pid = win32process.GetWindowThreadProcessId(hWnd)
if pid == oarhWnd[0]:
oarhWnd.append(hWnd)
# adapted from https://gist.github.com/andreafortuna/20d19ac02393264930b3d331fe66a6f6
def getProcessesNamed(pszProcName):
WMI = GetObject('winmgmts:')
arProcesses = WMI.ExecQuery('select * from Win32_Process where Name="%s"' %(pszProcName))
if len(arProcesses) == 0:
return []
return arProcesses
# signals to blocking functions that their time has expired
def signal_timeout(event, n):
event.wait(n)
event.set()
# main code
def set_thunderbird_in_foreground():
# wait up to 10 seconds for Thunderbird to spawn and have its window ready
# if this elapses, probably Thunderbird crashed; as we cannot exit the loops
# below, we start a thread that will kill the app after the time elapses;
# of course, this is one way of doing this, customize to your own needs
event = Event()
thread = Thread(target = signal_timeout, args = (event, 10, ))
thread.start()
# check if Thunderbird is already running and save its PID
pid = 0
match = getProcessesNamed("thunderbird.exe")
if len(match) > 0:
pid = match[0].Properties_('ProcessId').Value
# extract path of Thunderbird executable from registry
hKey = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE,
"SOFTWARE\\Clients\\Mail\\Mozilla Thunderbird\\shell\\open\\command",
0,
win32con.KEY_READ)
pszTbPath = win32api.RegQueryValueEx(hKey, "")[0]
# start Thunderbird; this is required even if the process is running for the
# case when Thunderbird is minimized to tray; Thunderbird detects when it already
# has a running instance and shows its window; however, there is still a chance,
# depending on what window is having focus and how this script is called, that
# Thunderbird won't be allowed by the system to become the active window; hence,
# Explorer is 'hooked' below to work around that, which will make Thunderbird
# the active window in all cases
stSI=win32process.STARTUPINFO()
_, _, ppid, _ = win32process.CreateProcess(None,
pszTbPath,
None,
None,
0,
0,
None,
None,
stSI)
# get PID of Thunderbird
if pid == 0:
pid = ppid
if pid == 0:
print("Cannot obtain PID of Thunderbird");
return 1
# wait for Thunderbird window to be shown on screen and save its window ID (hWnd)
hWnd = 0
while not event.isSet():
arhWnd = []
arhWnd.append(pid)
win32gui.EnumWindows(EnumWindowProc,
arhWnd)
if len(arhWnd) > 1:
hWnd = arhWnd[1]
break
if event.isSet():
print("Cannot obtain window ID of Thunderbird");
return 2
# obtain the address of "SetForegroundWindow" from module "User32"
hUser32 = kernel32.GetModuleHandleA("User32".encode("ascii"));
if hUser32 == 0:
print("Cannot obtain handle of User32");
return 3
hAdrSetForegroundWindow = kernel32.GetProcAddress(hUser32,
"SetForegroundWindow".encode("ascii"))
if hAdrSetForegroundWindow == 0:
print("Cannot obtain address of SetForegroundWindow");
return 4
# get PID of Explorer (using WMI) and request a handle to the process from the system
pid = getProcessesNamed("explorer.exe")[0].Properties_('ProcessId').Value
if pid == 0:
print("Cannot find PID of Explorer.")
return 5
hProcess = kernel32.OpenProcess(0x1F0FFF,
False,
pid)
if not hProcess:
print("Couldn't acquire a handle to PID: %s" % pid)
return 6
# create the remote thread in Explorer that will run SetForegroundWindow
# with Thunderbird's window ID; the thread is started automatically by the call
if not kernel32.CreateRemoteThread(hProcess,
None,
0,
hAdrSetForegroundWindow,
hWnd,
0,
c_ulong(0)):
kernel32.CloseHandle(hProcess)
print("Failed to inject shellcode. Exiting.")
return 7
# tell the system we are no longer using the handle to Explorer
kernel32.CloseHandle(hProcess)
event.set()
thread.join()
return 0
sys.exit(set_thunderbird_in_foreground())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment