injectlib 0.0.2__tar.gz → 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {injectlib-0.0.2 → injectlib-0.0.3}/MANIFEST.in +5 -1
- {injectlib-0.0.2/src/injectlib.egg-info → injectlib-0.0.3}/PKG-INFO +3 -2
- {injectlib-0.0.2 → injectlib-0.0.3}/README.md +14 -8
- {injectlib-0.0.2 → injectlib-0.0.3}/setup.py +22 -7
- injectlib-0.0.3/src/injectlib/__init__.py +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/api.py +16 -3
- injectlib-0.0.3/src/injectlib/cfuncs.py +245 -0
- injectlib-0.0.3/src/injectlib/channel.py +130 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/libs/dotnet/x64/bootstrap.dll +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/libs/dotnet/x64/worker_wpf.dll +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/libs/hook/x64/pywinmsg64.dll +0 -0
- injectlib-0.0.3/src/injectlib/libs/qt5/x64/qt5_srv.dll +0 -0
- injectlib-0.0.3/src/injectlib/libs/qt5/x64/qt5_srv.so +0 -0
- injectlib-0.0.3/src/injectlib/libs/qt5/x86/qt5_srv.dll +0 -0
- injectlib-0.0.3/src/injectlib/libs/qt6/x64/qt6_srv.dll +0 -0
- injectlib-0.0.3/src/injectlib/libs/qt6/x64/qt6_srv.so +0 -0
- injectlib-0.0.3/src/injectlib/linux_injector.py +112 -0
- {injectlib-0.0.2 → injectlib-0.0.3/src/injectlib.egg-info}/PKG-INFO +3 -2
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib.egg-info/SOURCES.txt +7 -1
- injectlib-0.0.3/src/injectlib.egg-info/requires.txt +3 -0
- injectlib-0.0.2/src/injectlib/cfuncs.py +0 -76
- injectlib-0.0.2/src/injectlib/channel.py +0 -69
- injectlib-0.0.2/src/injectlib/libs/qt/x64/qt_srv.dll +0 -0
- injectlib-0.0.2/src/injectlib.egg-info/requires.txt +0 -1
- {injectlib-0.0.2 → injectlib-0.0.3}/LICENSE +0 -0
- /injectlib-0.0.2/src/injectlib/__init__.py → /injectlib-0.0.3/pyproject.toml +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/setup.cfg +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/injector.py +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/libs/dotnet/x64/Newtonsoft.Json.dll +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/libs/dotnet/x86/Newtonsoft.Json.dll +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/libs/dotnet/x86/bootstrap.dll +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/libs/dotnet/x86/worker_wpf.dll +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/libs/hook/x64/pywinmsg64u.dll +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/libs/hook/x86/pywinmsg32.dll +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/libs/hook/x86/pywinmsg32u.dll +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib/sysinfo.py +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib.egg-info/dependency_links.txt +0 -0
- {injectlib-0.0.2 → injectlib-0.0.3}/src/injectlib.egg-info/top_level.txt +0 -0
|
@@ -13,4 +13,8 @@ include src/injectlib/libs/hook/x86/pywinmsg32u.dll
|
|
|
13
13
|
include src/injectlib/libs/hook/x64/pywinmsg64.dll
|
|
14
14
|
include src/injectlib/libs/hook/x64/pywinmsg64u.dll
|
|
15
15
|
|
|
16
|
-
include src/injectlib/libs/
|
|
16
|
+
include src/injectlib/libs/qt5/x86/qt5_srv.dll
|
|
17
|
+
include src/injectlib/libs/qt5/x64/qt5_srv.dll
|
|
18
|
+
include src/injectlib/libs/qt5/x64/qt5_srv.so
|
|
19
|
+
include src/injectlib/libs/qt6/x64/qt6_srv.dll
|
|
20
|
+
include src/injectlib/libs/qt6/x64/qt6_srv.so
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: injectlib
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: A set of Python modules to inject DLLs into applications for the Microsoft Windows
|
|
5
5
|
Home-page: https://github.com/pywinauto/injectlib
|
|
6
6
|
Author: Vasily Ryabov, Ilya Naumov, Boris Galochkin, Alexander Makarov and contributors
|
|
@@ -9,6 +9,7 @@ License: BSD-3-Clause
|
|
|
9
9
|
Project-URL: GitHub, https://github.com/pywinauto/injectlib
|
|
10
10
|
Keywords: windows gui .net inject testing test desktop dll wpf qt
|
|
11
11
|
Platform: win32
|
|
12
|
+
Platform: linux
|
|
12
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
14
|
Classifier: Environment :: Console
|
|
14
15
|
Classifier: Intended Audience :: Developers
|
|
@@ -28,7 +29,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
28
29
|
Classifier: Topic :: Software Development :: Testing
|
|
29
30
|
Requires-Python: >=3.7
|
|
30
31
|
License-File: LICENSE
|
|
31
|
-
Requires-Dist: pywin32
|
|
32
|
+
Requires-Dist: pywin32; platform_system == "Windows"
|
|
32
33
|
Dynamic: author
|
|
33
34
|
Dynamic: author-email
|
|
34
35
|
Dynamic: classifier
|
|
@@ -52,28 +52,34 @@ Link to PyPi: <https://pypi.org/project/injectlib/>
|
|
|
52
52
|
* MSBuild
|
|
53
53
|
* MSVC >= 143
|
|
54
54
|
* cmake >= 3.26.3
|
|
55
|
-
* Qt >= 5.15.2, < 6
|
|
55
|
+
* Qt 5 >= 5.15.2, < 6
|
|
56
|
+
* Qt 6
|
|
56
57
|
|
|
57
58
|
**NOTE**: You may use PyCharm + Visual Studio with C++/C# components for
|
|
58
59
|
development
|
|
59
60
|
|
|
60
61
|
### Building Qt DLL
|
|
61
62
|
|
|
62
|
-
This package compiles C++ binaries that link against **Qt 5** (>=5.15.2, <6)
|
|
63
|
-
Qt is not bundled and must be installed separately before building.
|
|
63
|
+
This package compiles C++ binaries that link against **Qt 5** (>=5.15.2, <6) and **Qt 6**. Qt is not bundled and must be installed separately before building
|
|
64
64
|
|
|
65
65
|
- **Windows** download options:
|
|
66
|
-
- download via [Qt Online Installer](https://www.qt.io/download) and select the MSVC
|
|
67
|
-
- via mirrors, for example, https://mirror.yandex.ru/mirrors/qt.io.
|
|
66
|
+
- download via [Qt Online Installer](https://www.qt.io/download) and select the MSVC components. Registration required. Only 64-bit supported. Installation might be banned for some countries.
|
|
67
|
+
- via mirrors, for example, https://mirror.yandex.ru/mirrors/qt.io.
|
|
68
|
+
- Base package for Qt 5.15.2 x64 can be found [here](https://mirror.yandex.ru/mirrors/qt.io/online/qtsdkrepository/windows_x86/desktop/qt5_5152/qt.qt5.5152.win64_msvc2019_64/5.15.2-0-202011130602qtbase-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86_64.7z). No registration required.
|
|
69
|
+
- Base package for Qt 5.15.2 x86 can be found [here](https://mirror.yandex.ru/mirrors/qt.io/online/qtsdkrepository/windows_x86/desktop/qt5_5152/qt.qt5.5152.win32_msvc2019/5.15.2-0-202011130602qtbase-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86.7z). No registration required.
|
|
70
|
+
- Base package for Qt 6.9.3 can be found [here](https://mirror.yandex.ru/mirrors/qt.io/online/qtsdkrepository/windows_x86/desktop/qt6_693/qt6_693/qt.qt6.693.win64_msvc2022_64/6.9.3-0-202509261208qtbase-Windows-Windows_11_23H2-MSVC2022-Windows-Windows_11_23H2-X86_64.7z)
|
|
68
71
|
|
|
69
|
-
Once installed, point CMake to your Qt
|
|
72
|
+
Once installed, point CMake to your Qt installations by setting `CMAKE_PREFIX_PATH` and add the Qt runtime directories to `PATH`.
|
|
73
|
+
|
|
74
|
+
For example:
|
|
70
75
|
|
|
71
76
|
```bash
|
|
72
77
|
# Windows (adjust version/compiler as needed)
|
|
73
|
-
set CMAKE_PREFIX_PATH=C
|
|
78
|
+
set CMAKE_PREFIX_PATH=C:\Qt\5.15.2\msvc2019;C:\Qt\5.15.2\msvc2019_64;C:\Qt\6.9.1\msvc2022_64
|
|
79
|
+
set PATH=C:\Qt\5.15.2\msvc2019\bin;C:\Qt\5.15.2\msvc2019_64\bin;C:\Qt\6.9.1\msvc2022_64\bin;%PATH%
|
|
74
80
|
```
|
|
75
81
|
|
|
76
|
-
If CMake still cannot find Qt, verify the
|
|
82
|
+
If CMake still cannot find Qt, verify the paths contain `lib/cmake/Qt5` and `lib/cmake/Qt6` subdirectories.
|
|
77
83
|
|
|
78
84
|
## License
|
|
79
85
|
|
|
@@ -12,6 +12,7 @@ from setuptools.dist import Distribution
|
|
|
12
12
|
x86_cmake_arch_name, x64_cmake_arch_name = 'Win32', 'x64'
|
|
13
13
|
x86_package_arch_name, x64_package_arch_name = 'x86', 'x64'
|
|
14
14
|
|
|
15
|
+
is_windows = sys.platform == 'win32'
|
|
15
16
|
is_64bit = sys.maxsize > 2**32
|
|
16
17
|
if is_64bit:
|
|
17
18
|
arch_names_map = { x64_cmake_arch_name: x64_package_arch_name }
|
|
@@ -19,8 +20,19 @@ else:
|
|
|
19
20
|
arch_names_map = { x86_cmake_arch_name: x86_package_arch_name }
|
|
20
21
|
|
|
21
22
|
build_dirname = 'build_'
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
if is_windows:
|
|
24
|
+
build_dll_dirs = ['./backends/dotnet/',
|
|
25
|
+
'./backends/hook/',
|
|
26
|
+
'./backends/qt5/']
|
|
27
|
+
package_dll_dirs = ['./src/injectlib/libs/dotnet/',
|
|
28
|
+
'./src/injectlib/libs/hook/',
|
|
29
|
+
'./src/injectlib/libs/qt5/']
|
|
30
|
+
else:
|
|
31
|
+
build_dll_dirs = ['./backends/qt5/']
|
|
32
|
+
package_dll_dirs = ['./src/injectlib/libs/qt5/']
|
|
33
|
+
if is_64bit:
|
|
34
|
+
build_dll_dirs.append('./backends/qt6/')
|
|
35
|
+
package_dll_dirs.append('./src/injectlib/libs/qt6/')
|
|
24
36
|
cmake_dirs = build_dll_dirs
|
|
25
37
|
|
|
26
38
|
|
|
@@ -35,7 +47,10 @@ class SdistWithDlls(sdist):
|
|
|
35
47
|
|
|
36
48
|
os.makedirs(build_dir, exist_ok=True)
|
|
37
49
|
|
|
38
|
-
|
|
50
|
+
cmake_command = ['cmake', '-B ' + build_dir, '-S ' + cmake_dir]
|
|
51
|
+
if is_windows:
|
|
52
|
+
cmake_command.extend(['-A', arch])
|
|
53
|
+
subprocess.check_call(cmake_command)
|
|
39
54
|
subprocess.check_call(['cmake', '--build', build_dir, '--config', 'Release'])
|
|
40
55
|
|
|
41
56
|
# copy DLLs to the package
|
|
@@ -43,7 +58,7 @@ class SdistWithDlls(sdist):
|
|
|
43
58
|
for build_dll_dir, package_dll_dir in zip(build_dll_dirs, package_dll_dirs):
|
|
44
59
|
for root, _, files in os.walk(build_dll_dir + build_dirname + arch):
|
|
45
60
|
for file in files:
|
|
46
|
-
if file.endswith('.dll'):
|
|
61
|
+
if file.endswith(('.dll', '.so')):
|
|
47
62
|
src_file_path = os.path.join(root, file)
|
|
48
63
|
dest_file_path = os.path.join(package_dll_dir + arch_names_map.get(arch), file)
|
|
49
64
|
os.makedirs(os.path.dirname(dest_file_path), exist_ok=True)
|
|
@@ -57,7 +72,7 @@ class BinaryDistribution(Distribution):
|
|
|
57
72
|
|
|
58
73
|
|
|
59
74
|
setup(name='injectlib',
|
|
60
|
-
version='0.0.
|
|
75
|
+
version='0.0.3',
|
|
61
76
|
description='A set of Python modules to inject DLLs into applications for the Microsoft Windows',
|
|
62
77
|
keywords="windows gui .net inject testing test desktop dll wpf qt",
|
|
63
78
|
url="https://github.com/pywinauto/injectlib",
|
|
@@ -80,7 +95,7 @@ precision and coverage than standard OS APIs like MS UI Automation API or
|
|
|
80
95
|
Win32 API. Other use cases are potentially possible at
|
|
81
96
|
your own risk according to the law and the project license.
|
|
82
97
|
""",
|
|
83
|
-
platforms=['win32'],
|
|
98
|
+
platforms=['win32', 'linux'],
|
|
84
99
|
|
|
85
100
|
packages=["injectlib"],
|
|
86
101
|
package_dir={"": "src"},
|
|
@@ -106,7 +121,7 @@ your own risk according to the law and the project license.
|
|
|
106
121
|
'Topic :: Software Development :: Libraries :: Python Modules',
|
|
107
122
|
'Topic :: Software Development :: Testing',
|
|
108
123
|
],
|
|
109
|
-
install_requires=['pywin32'],
|
|
124
|
+
install_requires=['pywin32; platform_system=="Windows"'],
|
|
110
125
|
python_requires='>=3.7',
|
|
111
126
|
cmdclass={'sdist': SdistWithDlls},
|
|
112
127
|
)
|
|
File without changes
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
+
import sys
|
|
3
4
|
|
|
4
|
-
from .injector import Injector
|
|
5
5
|
from .channel import Pipe
|
|
6
|
+
if sys.platform == "win32":
|
|
7
|
+
from .injector import Injector
|
|
8
|
+
else:
|
|
9
|
+
from .linux_injector import LinuxInjector as Injector
|
|
6
10
|
|
|
7
11
|
logger = logging.getLogger(__package__)
|
|
8
12
|
|
|
@@ -51,6 +55,11 @@ class Singleton(type):
|
|
|
51
55
|
class ConnectionManager(object, metaclass=Singleton):
|
|
52
56
|
def __init__(self):
|
|
53
57
|
self._pipes = {}
|
|
58
|
+
self._backend_config = {}
|
|
59
|
+
|
|
60
|
+
def register_backend(self, pid, backend_name, dll_name):
|
|
61
|
+
"""Register backend to use when injecting into pid. Must be called before the first call_action for that pid."""
|
|
62
|
+
self._backend_config[pid] = (backend_name, dll_name)
|
|
54
63
|
|
|
55
64
|
def _get_pipe(self, pid):
|
|
56
65
|
if pid not in self._pipes:
|
|
@@ -65,8 +74,12 @@ class ConnectionManager(object, metaclass=Singleton):
|
|
|
65
74
|
return pipe
|
|
66
75
|
else:
|
|
67
76
|
logger.info('Pipe {} not found, injecting dll to the process'.format(pipe_name))
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
# Fallback to dotnet to keep compatibility
|
|
78
|
+
backend_name, dll_name = self._backend_config.get(pid, ('dotnet', 'bootstrap'))
|
|
79
|
+
Injector(pid, backend_name, dll_name)
|
|
80
|
+
if not pipe.connect():
|
|
81
|
+
raise InjectedRuntimeError(
|
|
82
|
+
"Injected server channel did not appear for pid {}".format(pid))
|
|
70
83
|
return pipe
|
|
71
84
|
|
|
72
85
|
def call_action(self, action_name, pid, **params):
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""Some functions already exists in pywinauto, but for correctly
|
|
2
|
+
injector work needs full redefinition with ctypes compatible
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from ctypes import Structure, sizeof, alignment
|
|
8
|
+
from ctypes import c_char_p, c_wchar_p
|
|
9
|
+
from ctypes import POINTER
|
|
10
|
+
|
|
11
|
+
if sys.platform == "win32":
|
|
12
|
+
import win32con
|
|
13
|
+
from ctypes import windll
|
|
14
|
+
from ctypes.wintypes import BOOL, DWORD, HANDLE, LPVOID, LPCVOID
|
|
15
|
+
else:
|
|
16
|
+
import ctypes
|
|
17
|
+
from ctypes import c_int, c_long, c_ulonglong, c_void_p, byref
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if sys.platform == "win32":
|
|
21
|
+
class SECURITY_ATTRIBUTES(Structure):
|
|
22
|
+
_fields_ = [
|
|
23
|
+
('nLength', DWORD),
|
|
24
|
+
('lpSecurityDescriptor', LPVOID),
|
|
25
|
+
('bInheritHandle', BOOL),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
assert sizeof(SECURITY_ATTRIBUTES) == 12 or sizeof(SECURITY_ATTRIBUTES) == 24, sizeof(SECURITY_ATTRIBUTES)
|
|
29
|
+
assert alignment(SECURITY_ATTRIBUTES) == 4 or alignment(SECURITY_ATTRIBUTES) == 8, alignment(SECURITY_ATTRIBUTES)
|
|
30
|
+
|
|
31
|
+
PAGE_READWRITE = win32con.PAGE_READWRITE
|
|
32
|
+
WAIT_TIMEOUT = win32con.WAIT_TIMEOUT
|
|
33
|
+
PROCESS_ALL_ACCESS = win32con.PROCESS_ALL_ACCESS
|
|
34
|
+
VIRTUAL_MEM = (win32con.MEM_RESERVE | win32con.MEM_COMMIT)
|
|
35
|
+
LPCSTR = LPCTSTR = c_char_p
|
|
36
|
+
LPWTSTR = c_wchar_p
|
|
37
|
+
LPDWORD = PDWORD = POINTER(DWORD)
|
|
38
|
+
LPTHREAD_START_ROUTINE = LPVOID
|
|
39
|
+
LPSECURITY_ATTRIBUTES = POINTER(SECURITY_ATTRIBUTES)
|
|
40
|
+
|
|
41
|
+
OpenProcess = windll.kernel32.OpenProcess
|
|
42
|
+
OpenProcess.restype = HANDLE
|
|
43
|
+
OpenProcess.argtypes = (DWORD, BOOL, DWORD)
|
|
44
|
+
|
|
45
|
+
VirtualAllocEx = windll.kernel32.VirtualAllocEx
|
|
46
|
+
VirtualAllocEx.restype = LPVOID
|
|
47
|
+
VirtualAllocEx.argtypes = (HANDLE, LPVOID, DWORD, DWORD, DWORD)
|
|
48
|
+
|
|
49
|
+
ReadProcessMemory = windll.kernel32.ReadProcessMemory
|
|
50
|
+
ReadProcessMemory.restype = BOOL
|
|
51
|
+
ReadProcessMemory.argtypes = (HANDLE, LPCVOID, LPVOID, DWORD, DWORD)
|
|
52
|
+
|
|
53
|
+
WriteProcessMemory = windll.kernel32.WriteProcessMemory
|
|
54
|
+
WriteProcessMemory.restype = BOOL
|
|
55
|
+
WriteProcessMemory.argtypes = (HANDLE, LPVOID, LPCVOID, DWORD, DWORD)
|
|
56
|
+
|
|
57
|
+
CreateRemoteThread = windll.kernel32.CreateRemoteThread
|
|
58
|
+
CreateRemoteThread.restype = HANDLE
|
|
59
|
+
CreateRemoteThread.argtypes = (HANDLE, LPSECURITY_ATTRIBUTES, DWORD, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD)
|
|
60
|
+
|
|
61
|
+
GetModuleHandleA = windll.kernel32.GetModuleHandleA
|
|
62
|
+
GetModuleHandleA.restype = HANDLE
|
|
63
|
+
GetModuleHandleA.argtypes = (LPCTSTR,)
|
|
64
|
+
|
|
65
|
+
LoadLibraryA = windll.kernel32.LoadLibraryA
|
|
66
|
+
LoadLibraryA.restype = HANDLE
|
|
67
|
+
LoadLibraryA.argtypes = (LPCTSTR,)
|
|
68
|
+
|
|
69
|
+
GetModuleHandleW = windll.kernel32.GetModuleHandleW
|
|
70
|
+
GetModuleHandleW.restype = HANDLE
|
|
71
|
+
GetModuleHandleW.argtypes = (LPWTSTR,)
|
|
72
|
+
|
|
73
|
+
LoadLibraryW = windll.kernel32.LoadLibraryW
|
|
74
|
+
LoadLibraryW.restype = HANDLE
|
|
75
|
+
LoadLibraryW.argtypes = (LPWTSTR,)
|
|
76
|
+
|
|
77
|
+
GetProcAddress = windll.kernel32.GetProcAddress
|
|
78
|
+
GetProcAddress.restype = LPVOID
|
|
79
|
+
GetProcAddress.argtypes = (HANDLE, LPCTSTR)
|
|
80
|
+
|
|
81
|
+
WaitForSingleObject = windll.kernel32.WaitForSingleObject
|
|
82
|
+
WaitForSingleObject.restype = DWORD
|
|
83
|
+
WaitForSingleObject.argtypes = (HANDLE, DWORD)
|
|
84
|
+
else:
|
|
85
|
+
PTRACE_PEEKDATA = 2
|
|
86
|
+
PTRACE_POKEDATA = 5
|
|
87
|
+
PTRACE_CONT = 7
|
|
88
|
+
PTRACE_GETREGS = 12
|
|
89
|
+
PTRACE_SETREGS = 13
|
|
90
|
+
PTRACE_ATTACH = 16
|
|
91
|
+
PTRACE_DETACH = 17
|
|
92
|
+
RTLD_NOW = 2
|
|
93
|
+
RTLD_GLOBAL = 0x100
|
|
94
|
+
SIGTRAP = 5
|
|
95
|
+
|
|
96
|
+
libc = ctypes.CDLL(None, use_errno=True)
|
|
97
|
+
try:
|
|
98
|
+
libdl = ctypes.CDLL("libdl.so.2", use_errno=True)
|
|
99
|
+
except OSError:
|
|
100
|
+
libdl = libc
|
|
101
|
+
|
|
102
|
+
ptrace = libc.ptrace
|
|
103
|
+
ptrace.restype = c_long
|
|
104
|
+
ptrace.argtypes = (c_int, c_int, c_void_p, c_void_p)
|
|
105
|
+
|
|
106
|
+
waitpid = libc.waitpid
|
|
107
|
+
waitpid.restype = c_int
|
|
108
|
+
waitpid.argtypes = (c_int, POINTER(c_int), c_int)
|
|
109
|
+
|
|
110
|
+
dlopen = libdl.dlopen
|
|
111
|
+
dlopen.restype = c_void_p
|
|
112
|
+
dlopen.argtypes = (c_char_p, c_int)
|
|
113
|
+
|
|
114
|
+
class UserRegsStruct(Structure):
|
|
115
|
+
_fields_ = [
|
|
116
|
+
("r15", c_ulonglong),
|
|
117
|
+
("r14", c_ulonglong),
|
|
118
|
+
("r13", c_ulonglong),
|
|
119
|
+
("r12", c_ulonglong),
|
|
120
|
+
("rbp", c_ulonglong),
|
|
121
|
+
("rbx", c_ulonglong),
|
|
122
|
+
("r11", c_ulonglong),
|
|
123
|
+
("r10", c_ulonglong),
|
|
124
|
+
("r9", c_ulonglong),
|
|
125
|
+
("r8", c_ulonglong),
|
|
126
|
+
("rax", c_ulonglong),
|
|
127
|
+
("rcx", c_ulonglong),
|
|
128
|
+
("rdx", c_ulonglong),
|
|
129
|
+
("rsi", c_ulonglong),
|
|
130
|
+
("rdi", c_ulonglong),
|
|
131
|
+
("orig_rax", c_ulonglong),
|
|
132
|
+
("rip", c_ulonglong),
|
|
133
|
+
("cs", c_ulonglong),
|
|
134
|
+
("eflags", c_ulonglong),
|
|
135
|
+
("rsp", c_ulonglong),
|
|
136
|
+
("ss", c_ulonglong),
|
|
137
|
+
("fs_base", c_ulonglong),
|
|
138
|
+
("gs_base", c_ulonglong),
|
|
139
|
+
("ds", c_ulonglong),
|
|
140
|
+
("es", c_ulonglong),
|
|
141
|
+
("fs", c_ulonglong),
|
|
142
|
+
("gs", c_ulonglong),
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
def _wifstopped(status):
|
|
146
|
+
return (status & 0xff) == 0x7f
|
|
147
|
+
|
|
148
|
+
def _wstopsig(status):
|
|
149
|
+
return (status >> 8) & 0xff
|
|
150
|
+
|
|
151
|
+
def _ptrace(request, pid, address=0, data=0):
|
|
152
|
+
ctypes.set_errno(0)
|
|
153
|
+
result = ptrace(request, pid, c_void_p(address), c_void_p(data))
|
|
154
|
+
if result == -1 and ctypes.get_errno() != 0:
|
|
155
|
+
raise OSError(ctypes.get_errno(), os.strerror(ctypes.get_errno()))
|
|
156
|
+
return result
|
|
157
|
+
|
|
158
|
+
def ptrace_attach(pid):
|
|
159
|
+
_ptrace(PTRACE_ATTACH, pid)
|
|
160
|
+
status = c_int()
|
|
161
|
+
if waitpid(pid, byref(status), 0) < 0:
|
|
162
|
+
raise OSError(ctypes.get_errno(), os.strerror(ctypes.get_errno()))
|
|
163
|
+
if not _wifstopped(status.value):
|
|
164
|
+
raise RuntimeError("target did not stop after ptrace attach")
|
|
165
|
+
|
|
166
|
+
def ptrace_detach(pid):
|
|
167
|
+
_ptrace(PTRACE_DETACH, pid)
|
|
168
|
+
|
|
169
|
+
def ptrace_continue(pid):
|
|
170
|
+
_ptrace(PTRACE_CONT, pid)
|
|
171
|
+
status = c_int()
|
|
172
|
+
if waitpid(pid, byref(status), 0) < 0:
|
|
173
|
+
raise OSError(ctypes.get_errno(), os.strerror(ctypes.get_errno()))
|
|
174
|
+
if not _wifstopped(status.value):
|
|
175
|
+
raise RuntimeError("target did not stop after remote call")
|
|
176
|
+
return _wstopsig(status.value)
|
|
177
|
+
|
|
178
|
+
def ptrace_getregs(pid):
|
|
179
|
+
regs = UserRegsStruct()
|
|
180
|
+
_ptrace(PTRACE_GETREGS, pid, 0, ctypes.addressof(regs))
|
|
181
|
+
return regs
|
|
182
|
+
|
|
183
|
+
def ptrace_setregs(pid, regs):
|
|
184
|
+
_ptrace(PTRACE_SETREGS, pid, 0, ctypes.addressof(regs))
|
|
185
|
+
|
|
186
|
+
def read_target(pid, address, size):
|
|
187
|
+
data = bytearray()
|
|
188
|
+
word_size = sizeof(c_long)
|
|
189
|
+
offset = 0
|
|
190
|
+
while offset < size:
|
|
191
|
+
word = _ptrace(PTRACE_PEEKDATA, pid, address + offset, 0)
|
|
192
|
+
chunk = ctypes.string_at(byref(c_long(word)), word_size)
|
|
193
|
+
data.extend(chunk[:min(word_size, size - offset)])
|
|
194
|
+
offset += word_size
|
|
195
|
+
return bytes(data)
|
|
196
|
+
|
|
197
|
+
def write_target(pid, address, data):
|
|
198
|
+
word_size = sizeof(c_long)
|
|
199
|
+
offset = 0
|
|
200
|
+
while offset < len(data):
|
|
201
|
+
chunk = data[offset:offset + word_size]
|
|
202
|
+
if len(chunk) != word_size:
|
|
203
|
+
current = bytearray(read_target(pid, address + offset, word_size))
|
|
204
|
+
current[:len(chunk)] = chunk
|
|
205
|
+
chunk = bytes(current)
|
|
206
|
+
word = c_long.from_buffer_copy(chunk)
|
|
207
|
+
_ptrace(PTRACE_POKEDATA, pid, address + offset, word.value & ((1 << (word_size * 8)) - 1))
|
|
208
|
+
offset += word_size
|
|
209
|
+
|
|
210
|
+
def module_base_from_maps(pid, module_basename):
|
|
211
|
+
with open("/proc/{}/maps".format(pid), "r") as maps_file:
|
|
212
|
+
for line in maps_file:
|
|
213
|
+
parts = line.split()
|
|
214
|
+
if len(parts) < 6 or "x" not in parts[1]:
|
|
215
|
+
continue
|
|
216
|
+
path = parts[-1]
|
|
217
|
+
if os.path.basename(path) != module_basename:
|
|
218
|
+
continue
|
|
219
|
+
start = int(parts[0].split("-", 1)[0], 16)
|
|
220
|
+
offset = int(parts[2], 16)
|
|
221
|
+
return start - offset
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
def local_module_for_address(address):
|
|
225
|
+
with open("/proc/{}/maps".format(os.getpid()), "r") as maps_file:
|
|
226
|
+
for line in maps_file:
|
|
227
|
+
parts = line.split()
|
|
228
|
+
if len(parts) < 6:
|
|
229
|
+
continue
|
|
230
|
+
start_s, end_s = parts[0].split("-", 1)
|
|
231
|
+
start = int(start_s, 16)
|
|
232
|
+
end = int(end_s, 16)
|
|
233
|
+
if start <= address < end:
|
|
234
|
+
return os.path.basename(parts[-1]), start - int(parts[2], 16)
|
|
235
|
+
return None, None
|
|
236
|
+
|
|
237
|
+
def remote_symbol_address(pid, local_symbol):
|
|
238
|
+
local_address = ctypes.cast(local_symbol, c_void_p).value
|
|
239
|
+
module_name, local_base = local_module_for_address(local_address)
|
|
240
|
+
if not module_name or not local_base:
|
|
241
|
+
return None
|
|
242
|
+
remote_base = module_base_from_maps(pid, module_name)
|
|
243
|
+
if not remote_base:
|
|
244
|
+
return None
|
|
245
|
+
return remote_base + (local_address - local_base)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import socket
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__package__)
|
|
8
|
+
|
|
9
|
+
if sys.platform == "win32":
|
|
10
|
+
import pywintypes
|
|
11
|
+
import win32file
|
|
12
|
+
import win32pipe
|
|
13
|
+
import winerror
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InjectedBrokenPipeError(Exception):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if sys.platform == "win32":
|
|
21
|
+
class Pipe(object):
|
|
22
|
+
def __init__(self, name):
|
|
23
|
+
self.name = name
|
|
24
|
+
self.handle = None
|
|
25
|
+
|
|
26
|
+
def connect(self, n_attempts=30, delay=1):
|
|
27
|
+
for i in range(n_attempts):
|
|
28
|
+
try:
|
|
29
|
+
self.handle = win32file.CreateFile(
|
|
30
|
+
r'\\.\pipe\{}'.format(self.name),
|
|
31
|
+
win32file.GENERIC_READ | win32file.GENERIC_WRITE,
|
|
32
|
+
0,
|
|
33
|
+
None,
|
|
34
|
+
win32file.OPEN_EXISTING,
|
|
35
|
+
0,
|
|
36
|
+
None
|
|
37
|
+
)
|
|
38
|
+
win32pipe.SetNamedPipeHandleState(
|
|
39
|
+
self.handle,
|
|
40
|
+
win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT,
|
|
41
|
+
None,
|
|
42
|
+
None,
|
|
43
|
+
)
|
|
44
|
+
logger.info('Connected to the pipe {}'.format(self.name))
|
|
45
|
+
break
|
|
46
|
+
except pywintypes.error as e:
|
|
47
|
+
if e.args[0] == winerror.ERROR_FILE_NOT_FOUND:
|
|
48
|
+
logger.warning('Attempt {}/{}: failed to connect to the pipe {}'.format(
|
|
49
|
+
i + 1, n_attempts, self.name))
|
|
50
|
+
time.sleep(delay)
|
|
51
|
+
else:
|
|
52
|
+
raise InjectedBrokenPipeError('Unexpected pipe error: {}'.format(e))
|
|
53
|
+
if self.handle is not None:
|
|
54
|
+
return True
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
def transact(self, string):
|
|
58
|
+
try:
|
|
59
|
+
# TODO get preferred encoding from application
|
|
60
|
+
self.write(string, 'utf-8')
|
|
61
|
+
resp = win32file.ReadFile(self.handle, 64 * 1024)
|
|
62
|
+
return resp[1].decode('utf-8')
|
|
63
|
+
except pywintypes.error as e:
|
|
64
|
+
if e.args[0] == winerror.ERROR_BROKEN_PIPE:
|
|
65
|
+
raise InjectedBrokenPipeError("Broken pipe")
|
|
66
|
+
else:
|
|
67
|
+
raise InjectedBrokenPipeError('Unexpected pipe error: {}'.format(e))
|
|
68
|
+
|
|
69
|
+
def close(self):
|
|
70
|
+
win32file.CloseHandle(self.handle)
|
|
71
|
+
|
|
72
|
+
def write(self, string, encoding='utf-8'):
|
|
73
|
+
"""Write string with the specified encoding to the named pipe."""
|
|
74
|
+
win32file.WriteFile(self.handle, string.encode(encoding))
|
|
75
|
+
win32file.FlushFileBuffers(self.handle)
|
|
76
|
+
|
|
77
|
+
else:
|
|
78
|
+
class Pipe(object):
|
|
79
|
+
def __init__(self, name):
|
|
80
|
+
self.name = name
|
|
81
|
+
self.path = "/tmp/injectlib_qt_{0}_{1}.sock".format(
|
|
82
|
+
os.getuid(), name.rsplit("_", 1)[-1])
|
|
83
|
+
|
|
84
|
+
def connect(self, n_attempts=30, delay=1):
|
|
85
|
+
for i in range(n_attempts):
|
|
86
|
+
client = None
|
|
87
|
+
try:
|
|
88
|
+
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
89
|
+
client.connect(self.path)
|
|
90
|
+
return True
|
|
91
|
+
except OSError:
|
|
92
|
+
pass
|
|
93
|
+
finally:
|
|
94
|
+
if client is not None:
|
|
95
|
+
try:
|
|
96
|
+
client.close()
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
if i < n_attempts - 1:
|
|
101
|
+
logger.warning('Attempt {}/{}: failed to connect to the socket {}'.format(
|
|
102
|
+
i + 1, n_attempts, self.path))
|
|
103
|
+
time.sleep(delay)
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
def transact(self, string):
|
|
107
|
+
client = None
|
|
108
|
+
try:
|
|
109
|
+
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
110
|
+
client.connect(self.path)
|
|
111
|
+
client.sendall(string.encode("utf-8"))
|
|
112
|
+
client.shutdown(socket.SHUT_WR)
|
|
113
|
+
chunks = []
|
|
114
|
+
while True:
|
|
115
|
+
chunk = client.recv(64 * 1024)
|
|
116
|
+
if not chunk:
|
|
117
|
+
break
|
|
118
|
+
chunks.append(chunk)
|
|
119
|
+
return b"".join(chunks).decode("utf-8")
|
|
120
|
+
except OSError as exc:
|
|
121
|
+
raise InjectedBrokenPipeError('Unexpected socket error: {}'.format(exc))
|
|
122
|
+
finally:
|
|
123
|
+
if client is not None:
|
|
124
|
+
try:
|
|
125
|
+
client.close()
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
def close(self):
|
|
130
|
+
pass
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import platform
|
|
3
|
+
import struct
|
|
4
|
+
|
|
5
|
+
from . import cfuncs
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LinuxInjector(object):
|
|
9
|
+
"""Load an injectlib shared object into a same-user Linux process."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, pid, backend_name, dll_name):
|
|
12
|
+
self.pid = int(pid)
|
|
13
|
+
self.backend_name = backend_name
|
|
14
|
+
self.dll_name = dll_name
|
|
15
|
+
self._validate_process()
|
|
16
|
+
self.library_path = self._library_path()
|
|
17
|
+
self._inject_shared_object()
|
|
18
|
+
|
|
19
|
+
def _validate_process(self):
|
|
20
|
+
proc_dir = "/proc/{}".format(self.pid)
|
|
21
|
+
if not os.path.isdir(proc_dir):
|
|
22
|
+
raise RuntimeError("Target process does not exist: pid={}".format(self.pid))
|
|
23
|
+
if platform.machine().lower() not in ("x86_64", "amd64"):
|
|
24
|
+
raise RuntimeError("Linux Qt attach currently supports x86_64 only")
|
|
25
|
+
|
|
26
|
+
def _library_path(self):
|
|
27
|
+
arch = "x64"
|
|
28
|
+
path = os.path.join(
|
|
29
|
+
os.path.dirname(os.path.realpath(__file__)),
|
|
30
|
+
"libs",
|
|
31
|
+
self.backend_name,
|
|
32
|
+
arch,
|
|
33
|
+
"{}.so".format(self.dll_name),
|
|
34
|
+
)
|
|
35
|
+
if not os.path.isfile(path):
|
|
36
|
+
raise RuntimeError("Linux Qt server library is missing: {}".format(path))
|
|
37
|
+
return path
|
|
38
|
+
|
|
39
|
+
def _inject_shared_object(self):
|
|
40
|
+
remote_dlopen = cfuncs.remote_symbol_address(self.pid, cfuncs.dlopen)
|
|
41
|
+
if not remote_dlopen:
|
|
42
|
+
raise RuntimeError("Cannot resolve remote dlopen address for pid {}".format(self.pid))
|
|
43
|
+
|
|
44
|
+
cfuncs.ptrace_attach(self.pid)
|
|
45
|
+
saved_payload = None
|
|
46
|
+
saved_code = None
|
|
47
|
+
saved_regs = None
|
|
48
|
+
stack_addr = None
|
|
49
|
+
code_len = None
|
|
50
|
+
try:
|
|
51
|
+
regs = cfuncs.ptrace_getregs(self.pid)
|
|
52
|
+
saved_regs = cfuncs.UserRegsStruct()
|
|
53
|
+
for field, _ in cfuncs.UserRegsStruct._fields_:
|
|
54
|
+
setattr(saved_regs, field, getattr(regs, field))
|
|
55
|
+
|
|
56
|
+
code, path_addr = self._remote_dlopen_stub(remote_dlopen, regs.rsp)
|
|
57
|
+
code_len = len(code)
|
|
58
|
+
payload = self.library_path.encode("utf-8") + b"\0"
|
|
59
|
+
payload_len = ((128 + len(payload) + 7) // 8) * 8
|
|
60
|
+
stack_addr = (regs.rsp - payload_len - 128) & ~0xF
|
|
61
|
+
path_addr = stack_addr + 128
|
|
62
|
+
code, _ = self._remote_dlopen_stub(remote_dlopen, regs.rsp, path_addr)
|
|
63
|
+
|
|
64
|
+
saved_payload = cfuncs.read_target(self.pid, stack_addr, payload_len)
|
|
65
|
+
saved_code = cfuncs.read_target(self.pid, regs.rip, code_len)
|
|
66
|
+
|
|
67
|
+
stack_payload = bytearray(payload_len)
|
|
68
|
+
stack_payload[128:128 + len(payload)] = payload
|
|
69
|
+
cfuncs.write_target(self.pid, stack_addr, bytes(stack_payload))
|
|
70
|
+
cfuncs.write_target(self.pid, regs.rip, code)
|
|
71
|
+
|
|
72
|
+
regs.rip = saved_regs.rip
|
|
73
|
+
regs.rsp = stack_addr
|
|
74
|
+
cfuncs.ptrace_setregs(self.pid, regs)
|
|
75
|
+
stop_signal = cfuncs.ptrace_continue(self.pid)
|
|
76
|
+
if stop_signal != cfuncs.SIGTRAP:
|
|
77
|
+
raise RuntimeError("Remote dlopen stopped with unexpected signal {}".format(stop_signal))
|
|
78
|
+
|
|
79
|
+
after = cfuncs.ptrace_getregs(self.pid)
|
|
80
|
+
if after.rax == 0:
|
|
81
|
+
raise RuntimeError("Remote dlopen returned NULL for {}".format(self.library_path))
|
|
82
|
+
finally:
|
|
83
|
+
if saved_payload is not None and stack_addr is not None:
|
|
84
|
+
cfuncs.write_target(self.pid, stack_addr, saved_payload)
|
|
85
|
+
if saved_code is not None and saved_regs is not None and code_len is not None:
|
|
86
|
+
cfuncs.write_target(self.pid, saved_regs.rip, saved_code[:code_len])
|
|
87
|
+
if saved_regs is not None:
|
|
88
|
+
cfuncs.ptrace_setregs(self.pid, saved_regs)
|
|
89
|
+
cfuncs.ptrace_detach(self.pid)
|
|
90
|
+
|
|
91
|
+
if not self._target_has_library():
|
|
92
|
+
raise RuntimeError("Remote dlopen did not map {} in pid {}".format(
|
|
93
|
+
self.library_path, self.pid))
|
|
94
|
+
|
|
95
|
+
def _remote_dlopen_stub(self, remote_dlopen, stack_pointer, path_addr=None):
|
|
96
|
+
if path_addr is None:
|
|
97
|
+
path_addr = stack_pointer
|
|
98
|
+
code = bytearray()
|
|
99
|
+
code.extend(b"\x48\xb8")
|
|
100
|
+
code.extend(struct.pack("<Q", remote_dlopen))
|
|
101
|
+
code.extend(b"\x48\xbf")
|
|
102
|
+
code.extend(struct.pack("<Q", path_addr))
|
|
103
|
+
code.extend(b"\xbe")
|
|
104
|
+
code.extend(struct.pack("<I", cfuncs.RTLD_NOW | cfuncs.RTLD_GLOBAL))
|
|
105
|
+
code.extend(b"\xff\xd0")
|
|
106
|
+
code.extend(b"\xcc")
|
|
107
|
+
return bytes(code), path_addr
|
|
108
|
+
|
|
109
|
+
def _target_has_library(self):
|
|
110
|
+
real_library = os.path.realpath(self.library_path)
|
|
111
|
+
with open("/proc/{}/maps".format(self.pid), "r") as maps_file:
|
|
112
|
+
return any(real_library in line for line in maps_file)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: injectlib
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: A set of Python modules to inject DLLs into applications for the Microsoft Windows
|
|
5
5
|
Home-page: https://github.com/pywinauto/injectlib
|
|
6
6
|
Author: Vasily Ryabov, Ilya Naumov, Boris Galochkin, Alexander Makarov and contributors
|
|
@@ -9,6 +9,7 @@ License: BSD-3-Clause
|
|
|
9
9
|
Project-URL: GitHub, https://github.com/pywinauto/injectlib
|
|
10
10
|
Keywords: windows gui .net inject testing test desktop dll wpf qt
|
|
11
11
|
Platform: win32
|
|
12
|
+
Platform: linux
|
|
12
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
13
14
|
Classifier: Environment :: Console
|
|
14
15
|
Classifier: Intended Audience :: Developers
|
|
@@ -28,7 +29,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
28
29
|
Classifier: Topic :: Software Development :: Testing
|
|
29
30
|
Requires-Python: >=3.7
|
|
30
31
|
License-File: LICENSE
|
|
31
|
-
Requires-Dist: pywin32
|
|
32
|
+
Requires-Dist: pywin32; platform_system == "Windows"
|
|
32
33
|
Dynamic: author
|
|
33
34
|
Dynamic: author-email
|
|
34
35
|
Dynamic: classifier
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
MANIFEST.in
|
|
3
3
|
README.md
|
|
4
|
+
pyproject.toml
|
|
4
5
|
setup.py
|
|
5
6
|
src/injectlib/__init__.py
|
|
6
7
|
src/injectlib/api.py
|
|
7
8
|
src/injectlib/cfuncs.py
|
|
8
9
|
src/injectlib/channel.py
|
|
9
10
|
src/injectlib/injector.py
|
|
11
|
+
src/injectlib/linux_injector.py
|
|
10
12
|
src/injectlib/sysinfo.py
|
|
11
13
|
src/injectlib.egg-info/PKG-INFO
|
|
12
14
|
src/injectlib.egg-info/SOURCES.txt
|
|
@@ -23,4 +25,8 @@ src/injectlib/libs/hook/x64/pywinmsg64.dll
|
|
|
23
25
|
src/injectlib/libs/hook/x64/pywinmsg64u.dll
|
|
24
26
|
src/injectlib/libs/hook/x86/pywinmsg32.dll
|
|
25
27
|
src/injectlib/libs/hook/x86/pywinmsg32u.dll
|
|
26
|
-
src/injectlib/libs/
|
|
28
|
+
src/injectlib/libs/qt5/x64/qt5_srv.dll
|
|
29
|
+
src/injectlib/libs/qt5/x64/qt5_srv.so
|
|
30
|
+
src/injectlib/libs/qt5/x86/qt5_srv.dll
|
|
31
|
+
src/injectlib/libs/qt6/x64/qt6_srv.dll
|
|
32
|
+
src/injectlib/libs/qt6/x64/qt6_srv.so
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
"""Some functions already exists in pywinauto, but for correctly
|
|
2
|
-
injector work needs full redefinition with ctypes compatible
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import win32con
|
|
6
|
-
from ctypes import Structure, sizeof, alignment
|
|
7
|
-
from ctypes import c_char_p, c_wchar_p
|
|
8
|
-
from ctypes import POINTER
|
|
9
|
-
from ctypes import windll
|
|
10
|
-
from ctypes.wintypes import BOOL, DWORD, HANDLE, LPVOID, LPCVOID
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class SECURITY_ATTRIBUTES(Structure):
|
|
14
|
-
_fields_ = [
|
|
15
|
-
('nLength', DWORD),
|
|
16
|
-
('lpSecurityDescriptor', LPVOID),
|
|
17
|
-
('bInheritHandle', BOOL),
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
assert sizeof(SECURITY_ATTRIBUTES) == 12 or sizeof(SECURITY_ATTRIBUTES) == 24, sizeof(SECURITY_ATTRIBUTES)
|
|
22
|
-
assert alignment(SECURITY_ATTRIBUTES) == 4 or alignment(SECURITY_ATTRIBUTES) == 8, alignment(SECURITY_ATTRIBUTES)
|
|
23
|
-
|
|
24
|
-
PAGE_READWRITE = win32con.PAGE_READWRITE
|
|
25
|
-
WAIT_TIMEOUT = win32con.WAIT_TIMEOUT
|
|
26
|
-
PROCESS_ALL_ACCESS = win32con.PROCESS_ALL_ACCESS
|
|
27
|
-
VIRTUAL_MEM = (win32con.MEM_RESERVE | win32con.MEM_COMMIT)
|
|
28
|
-
LPCSTR = LPCTSTR = c_char_p
|
|
29
|
-
LPWTSTR = c_wchar_p
|
|
30
|
-
LPDWORD = PDWORD = POINTER(DWORD)
|
|
31
|
-
LPTHREAD_START_ROUTINE = LPVOID
|
|
32
|
-
LPSECURITY_ATTRIBUTES = POINTER(SECURITY_ATTRIBUTES)
|
|
33
|
-
|
|
34
|
-
OpenProcess = windll.kernel32.OpenProcess
|
|
35
|
-
OpenProcess.restype = HANDLE
|
|
36
|
-
OpenProcess.argtypes = (DWORD, BOOL, DWORD)
|
|
37
|
-
|
|
38
|
-
VirtualAllocEx = windll.kernel32.VirtualAllocEx
|
|
39
|
-
VirtualAllocEx.restype = LPVOID
|
|
40
|
-
VirtualAllocEx.argtypes = (HANDLE, LPVOID, DWORD, DWORD, DWORD)
|
|
41
|
-
|
|
42
|
-
ReadProcessMemory = windll.kernel32.ReadProcessMemory
|
|
43
|
-
ReadProcessMemory.restype = BOOL
|
|
44
|
-
ReadProcessMemory.argtypes = (HANDLE, LPCVOID, LPVOID, DWORD, DWORD)
|
|
45
|
-
|
|
46
|
-
WriteProcessMemory = windll.kernel32.WriteProcessMemory
|
|
47
|
-
WriteProcessMemory.restype = BOOL
|
|
48
|
-
WriteProcessMemory.argtypes = (HANDLE, LPVOID, LPCVOID, DWORD, DWORD)
|
|
49
|
-
|
|
50
|
-
CreateRemoteThread = windll.kernel32.CreateRemoteThread
|
|
51
|
-
CreateRemoteThread.restype = HANDLE
|
|
52
|
-
CreateRemoteThread.argtypes = (HANDLE, LPSECURITY_ATTRIBUTES, DWORD, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD)
|
|
53
|
-
|
|
54
|
-
GetModuleHandleA = windll.kernel32.GetModuleHandleA
|
|
55
|
-
GetModuleHandleA.restype = HANDLE
|
|
56
|
-
GetModuleHandleA.argtypes = (LPCTSTR,)
|
|
57
|
-
|
|
58
|
-
LoadLibraryA = windll.kernel32.LoadLibraryA
|
|
59
|
-
LoadLibraryA.restype = HANDLE
|
|
60
|
-
LoadLibraryA.argtypes = (LPCTSTR,)
|
|
61
|
-
|
|
62
|
-
GetModuleHandleW = windll.kernel32.GetModuleHandleW
|
|
63
|
-
GetModuleHandleW.restype = HANDLE
|
|
64
|
-
GetModuleHandleW.argtypes = (LPWTSTR,)
|
|
65
|
-
|
|
66
|
-
LoadLibraryW = windll.kernel32.LoadLibraryW
|
|
67
|
-
LoadLibraryW.restype = HANDLE
|
|
68
|
-
LoadLibraryW.argtypes = (LPWTSTR,)
|
|
69
|
-
|
|
70
|
-
GetProcAddress = windll.kernel32.GetProcAddress
|
|
71
|
-
GetProcAddress.restype = LPVOID
|
|
72
|
-
GetProcAddress.argtypes = (HANDLE, LPCTSTR)
|
|
73
|
-
|
|
74
|
-
WaitForSingleObject = windll.kernel32.WaitForSingleObject
|
|
75
|
-
WaitForSingleObject.restype = DWORD
|
|
76
|
-
WaitForSingleObject.argtypes = (HANDLE, DWORD)
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import pywintypes
|
|
2
|
-
import time
|
|
3
|
-
import win32file
|
|
4
|
-
import win32pipe
|
|
5
|
-
import winerror
|
|
6
|
-
import logging
|
|
7
|
-
|
|
8
|
-
logger = logging.getLogger(__package__)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class InjectedBrokenPipeError(Exception):
|
|
12
|
-
pass
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Pipe(object):
|
|
16
|
-
def __init__(self, name):
|
|
17
|
-
self.name = name
|
|
18
|
-
self.handle = None
|
|
19
|
-
|
|
20
|
-
def connect(self, n_attempts=30, delay=1):
|
|
21
|
-
for i in range(n_attempts):
|
|
22
|
-
try:
|
|
23
|
-
self.handle = win32file.CreateFile(
|
|
24
|
-
r'\\.\pipe\{}'.format(self.name),
|
|
25
|
-
win32file.GENERIC_READ | win32file.GENERIC_WRITE,
|
|
26
|
-
0,
|
|
27
|
-
None,
|
|
28
|
-
win32file.OPEN_EXISTING,
|
|
29
|
-
0,
|
|
30
|
-
None
|
|
31
|
-
)
|
|
32
|
-
win32pipe.SetNamedPipeHandleState(
|
|
33
|
-
self.handle,
|
|
34
|
-
win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT,
|
|
35
|
-
None,
|
|
36
|
-
None,
|
|
37
|
-
)
|
|
38
|
-
logger.info('Connected to the pipe {}'.format(self.name))
|
|
39
|
-
break
|
|
40
|
-
except pywintypes.error as e:
|
|
41
|
-
if e.args[0] == winerror.ERROR_FILE_NOT_FOUND:
|
|
42
|
-
logger.warning('Attempt {}/{}: failed to connect to the pipe {}'.format(i + 1, n_attempts,
|
|
43
|
-
self.name))
|
|
44
|
-
time.sleep(delay)
|
|
45
|
-
else:
|
|
46
|
-
raise InjectedBrokenPipeError('Unexpected pipe error: {}'.format(e))
|
|
47
|
-
if self.handle is not None:
|
|
48
|
-
return True
|
|
49
|
-
return False
|
|
50
|
-
|
|
51
|
-
def transact(self, string):
|
|
52
|
-
try:
|
|
53
|
-
# TODO get preferred encoding from application
|
|
54
|
-
self.write(string, 'utf-8')
|
|
55
|
-
resp = win32file.ReadFile(self.handle, 64 * 1024)
|
|
56
|
-
return resp[1].decode('utf-8')
|
|
57
|
-
except pywintypes.error as e:
|
|
58
|
-
if e.args[0] == winerror.ERROR_BROKEN_PIPE:
|
|
59
|
-
raise InjectedBrokenPipeError("Broken pipe")
|
|
60
|
-
else:
|
|
61
|
-
raise InjectedBrokenPipeError('Unexpected pipe error: {}'.format(e))
|
|
62
|
-
|
|
63
|
-
def close(self):
|
|
64
|
-
win32file.CloseHandle(self.handle)
|
|
65
|
-
|
|
66
|
-
def write(self, string, encoding='utf-8'):
|
|
67
|
-
"""Write string with the specified encoding to the named pipe."""
|
|
68
|
-
win32file.WriteFile(self.handle, string.encode(encoding))
|
|
69
|
-
win32file.FlushFileBuffers(self.handle)
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pywin32
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|