forked from StormExecute/InputSwitcherX
-
Notifications
You must be signed in to change notification settings - Fork 0
/
disable_switcher.py
184 lines (150 loc) · 5.14 KB
/
disable_switcher.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
from typing import List, Tuple
from binascii import unhexlify, hexlify
from pathlib import Path
from ctypes import windll
import logging as log
import shutil
import time
import sys
import os
log.root.setLevel(log.INFO)
WIN_PATH = Path(os.environ["WINDIR"])
SYS32_PATH = (WIN_PATH / "System32").resolve()
WIN_SXS_PATH = WIN_PATH / "WinSxS"
EXEC_PATH = Path(__file__).parent.absolute()
BACKUPS_PATH = EXEC_PATH / "backup"
DLL_NAME = "InputSwitch.dll"
INROW_REPLACE: Tuple[str, ...] = (
"ff",
"ff",
"83",
"f8",
"ff",
"33",
"c0",
)
def bulk_exec(*command: str):
for i in command:
os.system(i)
class ScriptException(Exception):
path: Path
type: int
def __init__(self, path: Path, text: str) -> None:
self.path = path
super().__init__(text)
class FileNotExists(ScriptException):
type = log.WARN
def __init__(self, path: Path) -> None:
super().__init__(path, "file not exists!")
class UnpatchableDLL(ScriptException):
type = log.ERROR
def __init__(self, path: Path) -> None:
super().__init__(path, "cant patch this dll!")
class Patch:
dirs: List[Path]
def __init__(self) -> None:
self.dirs = [SYS32_PATH]
for item in WIN_SXS_PATH.iterdir():
if not item.is_dir():
continue
if "inputswitch" in item.name:
self.dirs.append(item)
def run(self):
has_errors = False
log.info("Процесс патчинга начинается. Отключение explorer.exe...")
os.system("taskkill /F /IM explorer.exe")
time.sleep(5)
for path in self.dirs:
try:
self.patch_dir(path)
except ScriptException as e:
if e.type >= log.ERROR:
has_errors = True
log.log(
e.type,
f"{e.path} : {e}"
)
os.system("start %windir%\\explorer.exe")
if has_errors:
log.info(
"Патч завершился с ошибками. Это есть не добрый знак.\n"
'Попытайтесь откатить изменения с помощью "python enable_switcher.py"'
)
sys.exit(1)
else:
log.info("Done!")
sys.exit(0)
def patch_dir(self, path: Path):
dll_path = path / DLL_NAME
if not dll_path.exists():
raise FileNotExists(dll_path)
bulk_exec(
# set group of admins as the owners of file
f'takeown /F "{dll_path}" /A',
# give the administrator group full access to this file
f'icacls "{dll_path}" /grant:r "*S-1-5-32-544":f'
)
# backup
backup_path = BACKUPS_PATH / path.name
if not backup_path.exists():
backup_path.mkdir(parents=True, exist_ok=True)
shutil.copyfile(str(dll_path), str(backup_path / DLL_NAME))
(backup_path / "info.txt").write_text(str(dll_path))
try:
self.patch_dll(dll_path)
finally:
bulk_exec(
# return the rights to trusted installer
f'icacls "{dll_path}" /setowner "NT SERVICE\\TrustedInstaller" /C /L /Q',
# give administrators and trusted installer read and execute permissions
f'icacls "{dll_path}" /grant:r "NT SERVICE\\TrustedInstaller":rx',
f'icacls "{dll_path}" /grant:r "*S-1-5-32-544":rx'
)
def patch_dll(self, dll_path: Path):
hexdata = (
hexlify(dll_path.read_bytes())
.decode("utf-8")
)
pointer = 0
hex_list: List[str] = list()
for i, h in enumerate(hexdata):
if not i & 1 and i != 0:
pointer += 1
if pointer < len(hex_list):
hex_list[pointer] += h
else:
hex_list.append(h)
in_row = 0
max_area = 40
res = False
for i, h in enumerate(hex_list):
if in_row >= 5:
if max_area <= 0:
log.debug("max area exceeded")
break
max_area -= 1
hex_list[i] = "90"
if in_row in range(5, 7) and INROW_REPLACE[in_row] == h:
in_row += 1
elif in_row == 7 and h in ("48", "8b"):
# final
hex_list[i] = h
hex_list[i - 1] = INROW_REPLACE[6]
hex_list[i - 2] = INROW_REPLACE[5]
res = True
break
elif in_row in range(0, 5) and INROW_REPLACE[in_row] == h:
in_row += 1
else:
in_row = 0
if not res:
raise UnpatchableDLL(dll_path)
with open(dll_path, "wb") as f:
for h in hex_list:
f.write(unhexlify(h))
log.info(f"{dll_path} : SUCCESSFUL PATCHING")
if __name__ == "__main__":
if not windll.shell32.IsUserAnAdmin():
print("Please run script as admin!")
sys.exit(1)
Patch().run()