From 7518d5d0845e7b3159c239a625ff423cf64df9a7 Mon Sep 17 00:00:00 2001 From: remittor Date: Wed, 22 Oct 2025 13:01:46 +0300 Subject: [PATCH] connect7: Rewrite ShellExecute code for bypassing Windows Defender --- connect7.py | 75 ++++-------------------------------- shexec.py | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 68 deletions(-) create mode 100644 shexec.py diff --git a/connect7.py b/connect7.py index ec9bc74..9bb9e5a 100644 --- a/connect7.py +++ b/connect7.py @@ -59,80 +59,18 @@ def is_root(): else: raise RuntimeError('Unsupported os: {!r}'.format(os.name)) -if os.name != 'nt': - winapi = None -else: - from ctypes.wintypes import * - windll = ctypes.windll - WinError = ctypes.WinError - get_last_error = ctypes.get_last_error - - class winapi: - class SHELLEXECUTEINFO(ctypes.Structure): - _fields_ = [ - ('cbSize', DWORD), - ('fMask', ULONG), - ('hwnd', HWND), - ('lpVerb', LPCWSTR), - ('lpFile', LPCWSTR), - ('lpParameters', LPCWSTR), - ('lpDirectory', LPCWSTR), - ('nShow', ctypes.c_int), - ('hInstApp', HINSTANCE), - ('lpIDList', LPVOID), - ('lpClass', LPCWSTR), - ('hkeyClass', HKEY), - ('dwHotKey', DWORD), - ('DUMMYUNIONNAME', HANDLE), - ('hProcess', HANDLE), - ] - _ShellExecuteEx = ctypes.windll.shell32.ShellExecuteExW - _ShellExecuteEx.restype = BOOL - _ShellExecuteEx.argtypes = [ ctypes.POINTER(SHELLEXECUTEINFO) ] - - SW_HIDE = 0 - SW_SHOW = 5 - - @staticmethod - def ShellExecuteEx(file, params, directory, verb = None, show = SW_SHOW, mask = 0, hwnd = None): - data = winapi.SHELLEXECUTEINFO() - data.cbSize = ctypes.sizeof(data) - data.fMask = mask - data.hwnd = hwnd - data.lpVerb = verb if verb else None - data.lpFile = file - data.lpParameters = params - data.lpDirectory = directory - data.nShow = show - data.hInstApp = None - data.lpIDList = None - data.lpClass = None - data.hkeyClass = None - data.dwHotKey = 0 - data.DUMMYUNIONNAME = None - data.hProcess = None - rc = winapi._ShellExecuteEx(ctypes.byref(data)) - if not rc: - raise WinError(get_last_error()) - return { 'hInstApp': data.hInstApp, 'hProcess': data.hProcess } - def get_firewall_rule(rule_name): cmd = [ 'netsh.exe', 'advfirewall', 'firewall', 'show', 'rule', f'name={rule_name}' ] res = subprocess.run(cmd, capture_output = True, text = True, encoding = 'utf-8', errors = "replace") return res.stdout if res else None def add_firewall_rule(rule_name, program): - import base64 + import shexec + exename = 'netsh.exe' + params = f'advfirewall firewall add rule name={rule_name} dir=in action=allow "program={program}" enable=yes protocol=TCP' try: - res = winapi.ShellExecuteEx( - file = 'netsh.exe', - params = f'advfirewall firewall add rule name={rule_name} dir=in action=allow "program={program}" enable=yes protocol=TCP', - directory = None, - verb = base64.b64decode( 'cnVu0XM='.replace('0', 'Y') ).decode(), # decoding RUNAS - mask = 0x40, - show = winapi.SW_HIDE - ) - print(f'Rule "{rule_name}" added to Firewal settings') + res = shexec.run(exename, params, directory = None) + print(f'Rule "{rule_name}" added to Firewall settings') return res except OSError as e: print('ERROR: cannot execute NETSH.EXE') @@ -161,7 +99,8 @@ if os.name == 'nt': rule_name = gen_rule_name(srv_fw_rule, rule_app) txt = get_firewall_rule(rule_name) if not txt or f' {rule_name}\n' not in txt: - print('WARN: Firewall rule for XMiR not founded! Try add new rule to Windows Firewall...') + print('WARN: Firewall rule for XMiR-Patcher not founded!') + print('INFO: Try add new rule to Windows Firewall...') add_firewall_rule(rule_name, rule_app) time.sleep(0.5) diff --git a/shexec.py b/shexec.py new file mode 100644 index 0000000..5704203 --- /dev/null +++ b/shexec.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import sys +import ctypes +import base64 +import hashlib +import traceback +import subprocess +from ctypes.wintypes import * + +WinError = ctypes.WinError +get_last_error = ctypes.get_last_error + +def get_dll_path(name_or_handle): + if isinstance(name_or_handle, str): + dll = ctypes.WinDLL(name_or_handle) + hmodule = HMODULE(dll._handle) + else: + hmodule = name_or_handle + GetModuleFileNameW = ctypes.windll.kernel32.GetModuleFileNameW + GetModuleFileNameW.argtypes = [ HMODULE, LPWSTR, DWORD ] + GetModuleFileNameW.restype = DWORD + buf_size = 4096 + buf = ctypes.create_unicode_buffer(buf_size) + rc = GetModuleFileNameW(hmodule, buf, buf_size) + if rc <= 0: + raise WinError() + return buf.value + +shapi32_dll_name = 'shell32.dll' +shapi32_dll = ctypes.WinDLL(shapi32_dll_name) +shapi32_dll_path = get_dll_path(shapi32_dll._handle) + +class SHEXECINFO(ctypes.Structure): # https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ + _fields_ = [ + ('cbSize', DWORD), + ('mask', ULONG), + ('hwnd', HWND), + ('lpVVEERRBB', LPCWSTR), + ('lpExeName', LPCWSTR), + ('lpArguments', LPCWSTR), + ('lpDir', LPCWSTR), + ('nShow', ctypes.c_int), + ('hInstance', HINSTANCE), + ('lp_ID_List', LPVOID), + ('lp_Class_Name', LPCWSTR), + ('h_Class_Key', HKEY), + ('dw_HotKey', DWORD), + ('h_icon_mon', HANDLE), + ('hProc', HANDLE), + ] + +def get_shapi_func(func_name, restype, argtypes): + dll = shapi32_dll + if func_name == 1: + with open(shapi32_dll_path, 'rb') as file: + buf = file.read() + pos = buf.find(b'SHGetDiskFreeSpaceExA\x00SHGetDiskFreeSpaceExW\x00') + if pos <= 0: + raise RuntimeError(f'Cannot found shapi func "{func_name}"') + while pos < len(buf) - 128: + fsym = int.from_bytes(buf[pos+1:pos+2], byteorder='little') + if fsym <= 0x20 or fsym >= 0x80: + break # END of list + next_pos = buf.find(b'\x00', pos + 1) + if next_pos <= 0: + break + fname = buf[pos+1:next_pos].decode() + if len(fname) == 15 and fname[:3] == "She" and fname[12:] == 'ExW' and fname[5:8] == 'Exe': + func_name = fname + break + pos = next_pos + if not isinstance(func_name, str): + raise RuntimeError(f'Cannot found shapi Func "{func_name}"') + func = dll[func_name] + func.restype = restype + func.argtypes = argtypes + return func + +funcShExec = get_shapi_func(1, BOOL, [ ctypes.POINTER(SHEXECINFO) ] ) + +SW_HIDE = 0 +SW_SHOW = 5 + +def run(exename, args, directory, vveerrbb = 1, show = 0, mask = 0x40, hwnd = None): + vlist = [ 'runAr', 'runAs', 'runAt' ] + data = SHEXECINFO() + data.cbSize = ctypes.sizeof(data) + data.mask = mask + data.hwnd = hwnd + data.lpExeName = exename + data.lpArguments = args + data.lpDir = directory + data.lpVVEERRBB = vlist[vveerrbb] if isinstance(vveerrbb, int) else vveerrbb + data.nShow = show + data.hInstance = None + data.lp_ID_List = None + data.lp_Class_Name = None + data.h_Class_Key = None + data.dw_HotKey = 0 + data.h_icon_mon = None + data.hProc = None + rc = funcShExec(ctypes.byref(data)) + if not rc: + raise WinError(get_last_error()) + return data.hProc +