injectlib 0.0.1__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.1/LICENSE +30 -0
- injectlib-0.0.1/MANIFEST.in +16 -0
- injectlib-0.0.1/PKG-INFO +42 -0
- injectlib-0.0.1/README.md +84 -0
- injectlib-0.0.1/setup.cfg +4 -0
- injectlib-0.0.1/setup.py +95 -0
- injectlib-0.0.1/src/injectlib/__init__.py +0 -0
- injectlib-0.0.1/src/injectlib/api.py +90 -0
- injectlib-0.0.1/src/injectlib/cfuncs.py +76 -0
- injectlib-0.0.1/src/injectlib/channel.py +69 -0
- injectlib-0.0.1/src/injectlib/injector.py +89 -0
- injectlib-0.0.1/src/injectlib/libs/dotnet/x64/Newtonsoft.Json.dll +0 -0
- injectlib-0.0.1/src/injectlib/libs/dotnet/x64/bootstrap.dll +0 -0
- injectlib-0.0.1/src/injectlib/libs/dotnet/x64/worker_wpf.dll +0 -0
- injectlib-0.0.1/src/injectlib/libs/dotnet/x86/Newtonsoft.Json.dll +0 -0
- injectlib-0.0.1/src/injectlib/libs/dotnet/x86/bootstrap.dll +0 -0
- injectlib-0.0.1/src/injectlib/libs/dotnet/x86/worker_wpf.dll +0 -0
- injectlib-0.0.1/src/injectlib/libs/hook/x64/pywinmsg64.dll +0 -0
- injectlib-0.0.1/src/injectlib/libs/hook/x64/pywinmsg64u.dll +0 -0
- injectlib-0.0.1/src/injectlib/libs/hook/x86/pywinmsg32.dll +0 -0
- injectlib-0.0.1/src/injectlib/libs/hook/x86/pywinmsg32u.dll +0 -0
- injectlib-0.0.1/src/injectlib/libs/qt/x64/qt_srv.dll +0 -0
- injectlib-0.0.1/src/injectlib/sysinfo.py +61 -0
- injectlib-0.0.1/src/injectlib.egg-info/PKG-INFO +42 -0
- injectlib-0.0.1/src/injectlib.egg-info/SOURCES.txt +26 -0
- injectlib-0.0.1/src/injectlib.egg-info/dependency_links.txt +1 -0
- injectlib-0.0.1/src/injectlib.egg-info/requires.txt +1 -0
- injectlib-0.0.1/src/injectlib.egg-info/top_level.txt +1 -0
injectlib-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vasily Ryabov, Ilya Naumov, Boris Galochkin, Alexander Makarov
|
|
4
|
+
https://github.com/pywinauto/injectlib/graphs/contributors
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
Redistribution and use in source and binary forms, with or without
|
|
8
|
+
modification, are permitted provided that the following conditions are met:
|
|
9
|
+
|
|
10
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
11
|
+
list of conditions and the following disclaimer.
|
|
12
|
+
|
|
13
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
14
|
+
this list of conditions and the following disclaimer in the documentation
|
|
15
|
+
and/or other materials provided with the distribution.
|
|
16
|
+
|
|
17
|
+
* Neither the name of the copyright holder nor the names of its
|
|
18
|
+
contributors may be used to endorse or promote products derived from
|
|
19
|
+
this software without specific prior written permission.
|
|
20
|
+
|
|
21
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
22
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
23
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
24
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
25
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
26
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
27
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
28
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
29
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
30
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
include src/injectlib/libs/dotnet/x86/bootstrap.dll
|
|
2
|
+
include src/injectlib/libs/dotnet/x86/Newtonsoft.Json.dll
|
|
3
|
+
include src/injectlib/libs/dotnet/x86/worker_wpf.dll
|
|
4
|
+
|
|
5
|
+
include src/injectlib/libs/dotnet/x64/bootstrap.dll
|
|
6
|
+
include src/injectlib/libs/dotnet/x64/Newtonsoft.Json.dll
|
|
7
|
+
include src/injectlib/libs/dotnet/x64/worker_wpf.dll
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
include src/injectlib/libs/hook/x86/pywinmsg32.dll
|
|
11
|
+
include src/injectlib/libs/hook/x86/pywinmsg32u.dll
|
|
12
|
+
|
|
13
|
+
include src/injectlib/libs/hook/x64/pywinmsg64.dll
|
|
14
|
+
include src/injectlib/libs/hook/x64/pywinmsg64u.dll
|
|
15
|
+
|
|
16
|
+
include src/injectlib/libs/qt/x64/qt_srv.dll
|
injectlib-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: injectlib
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A set of Python modules to inject DLLs into applications for the Microsoft Windows
|
|
5
|
+
Home-page: https://github.com/pywinauto/injectlib
|
|
6
|
+
Author: Vasily Ryabov, Ilya Naumov, Boris Galochkin, Alexander Makarov
|
|
7
|
+
Author-email: vasily.v.ryabov@gmail.com
|
|
8
|
+
License: BSD 3-clause
|
|
9
|
+
Keywords: windows gui .net inject testing test desktop dll wpf qt
|
|
10
|
+
Platform: win32
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Topic :: Software Development :: Testing
|
|
25
|
+
Requires-Python: >=3.7
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: pywin32
|
|
28
|
+
Dynamic: author
|
|
29
|
+
Dynamic: author-email
|
|
30
|
+
Dynamic: classifier
|
|
31
|
+
Dynamic: description
|
|
32
|
+
Dynamic: home-page
|
|
33
|
+
Dynamic: keywords
|
|
34
|
+
Dynamic: license
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
Dynamic: platform
|
|
37
|
+
Dynamic: requires-dist
|
|
38
|
+
Dynamic: requires-python
|
|
39
|
+
Dynamic: summary
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
It allows to inject DLls into applications for the Microsoft Windows.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# injectlib
|
|
2
|
+
|
|
3
|
+
injectlib is a Python package that allows you to inject DLLs into running
|
|
4
|
+
processes on Windows (32 or 64-bit).
|
|
5
|
+
|
|
6
|
+
This can be useful for various tasks, such as debugging or adding
|
|
7
|
+
functionality to existing applications.
|
|
8
|
+
|
|
9
|
+
This is initially created for desktop GUI automation purpose
|
|
10
|
+
(project pywinauto), because an injected DLL can access GUI widgets' text
|
|
11
|
+
properties with much better precision and coverage than standard OS APIs like
|
|
12
|
+
MS UI Automation API or Win32 API. Other use cases are potentially possible at
|
|
13
|
+
your own risk according to the law and the project license.
|
|
14
|
+
|
|
15
|
+
It contains:
|
|
16
|
+
|
|
17
|
+
* initial DLL to be injected which is written in C,
|
|
18
|
+
* server-side DLL that can exchange data between injected DLL (server) and
|
|
19
|
+
Python process (client),
|
|
20
|
+
* managed DLL which is able to get text properties of WPF applications,
|
|
21
|
+
* DLL which is able to get text properties of Qt applications,
|
|
22
|
+
* Python code to initiate the injection,
|
|
23
|
+
* and client Python code for data exchange with an injected DLL.
|
|
24
|
+
|
|
25
|
+
Link to PyPi: <https://pypi.org/project/injectlib/>
|
|
26
|
+
|
|
27
|
+
## How to use
|
|
28
|
+
|
|
29
|
+
python.exe example_injector.py \<Process name\> \<path to dll\>
|
|
30
|
+
|
|
31
|
+
## Development
|
|
32
|
+
|
|
33
|
+
1. Clone repository: <https://github.com/pywinauto/injectlib.git>
|
|
34
|
+
2. Create new features or improve exiting (for the injectlib package)
|
|
35
|
+
3. After work run setup.py from project root to check standalone
|
|
36
|
+
|
|
37
|
+
python.exe setup.py bdist_wheel
|
|
38
|
+
|
|
39
|
+
## Requirements
|
|
40
|
+
|
|
41
|
+
### For use as package
|
|
42
|
+
|
|
43
|
+
* Python >= 3.7
|
|
44
|
+
* pywin32 >= 306
|
|
45
|
+
|
|
46
|
+
### For development purpose
|
|
47
|
+
|
|
48
|
+
* setuptools >= 65.5.1
|
|
49
|
+
* wheel >= 0.38.4
|
|
50
|
+
* .NET Framework Targeting Pack >= 4.8
|
|
51
|
+
* .NET Compiler Platform
|
|
52
|
+
* MSBuild
|
|
53
|
+
* MSVC >= 143
|
|
54
|
+
* cmake >= 3.26.3
|
|
55
|
+
* Qt >= 5.15.2, < 6
|
|
56
|
+
|
|
57
|
+
**NOTE**: You may use PyCharm + Visual Studio with C++/C# components for
|
|
58
|
+
development
|
|
59
|
+
|
|
60
|
+
### Building Qt DLL
|
|
61
|
+
|
|
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.
|
|
64
|
+
|
|
65
|
+
- **Windows** download options:
|
|
66
|
+
- download via [Qt Online Installer](https://www.qt.io/download) and select the MSVC 2019 64-bit component. Registration required. Installation might be banned for some countries
|
|
67
|
+
- via mirrors, for example, https://mirror.yandex.ru/mirrors/qt.io. Base package for Qt 5.15.2 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.
|
|
68
|
+
|
|
69
|
+
Once installed, point CMake to your Qt installation by setting `CMAKE_PREFIX_PATH`:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Windows (adjust version/compiler as needed)
|
|
73
|
+
set CMAKE_PREFIX_PATH=C:/Qt/5.15.2/msvc2019_64
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If CMake still cannot find Qt, verify the path contains a `lib/cmake/Qt5` subdirectory.
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
This software is open-source under the BSD 3-Clause License.
|
|
81
|
+
|
|
82
|
+
See the LICENSE file in this repository.
|
|
83
|
+
|
|
84
|
+
Dependencies are licensed by their own licenses.
|
injectlib-0.0.1/setup.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Install and build injectlib distributions"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from setuptools import setup
|
|
8
|
+
from setuptools.command.sdist import sdist
|
|
9
|
+
from setuptools.dist import Distribution
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
x86_cmake_arch_name, x64_cmake_arch_name = 'Win32', 'x64'
|
|
13
|
+
x86_package_arch_name, x64_package_arch_name = 'x86', 'x64'
|
|
14
|
+
|
|
15
|
+
is_64bit = sys.maxsize > 2**32
|
|
16
|
+
if is_64bit:
|
|
17
|
+
arch_names_map = { x64_cmake_arch_name: x64_package_arch_name }
|
|
18
|
+
else:
|
|
19
|
+
arch_names_map = { x86_cmake_arch_name: x86_package_arch_name }
|
|
20
|
+
|
|
21
|
+
build_dirname = 'build_'
|
|
22
|
+
build_dll_dirs = ['./backends/dotnet/', './backends/hook/', './backends/qt/']
|
|
23
|
+
package_dll_dirs = ['./src/injectlib/libs/dotnet/', './src/injectlib/libs/hook/', './src/injectlib/libs/qt/']
|
|
24
|
+
cmake_dirs = build_dll_dirs
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SdistWithDlls(sdist):
|
|
28
|
+
"""Custom build command that compiles DLLs before packaging Python files."""
|
|
29
|
+
|
|
30
|
+
def run(self):
|
|
31
|
+
# cmake build for DLLs
|
|
32
|
+
for arch in arch_names_map:
|
|
33
|
+
for cmake_dir in cmake_dirs:
|
|
34
|
+
build_dir = cmake_dir + build_dirname + arch
|
|
35
|
+
|
|
36
|
+
os.makedirs(build_dir, exist_ok=True)
|
|
37
|
+
|
|
38
|
+
subprocess.check_call(['cmake', '-B ' + build_dir, '-S ' + cmake_dir, '-A ' + arch])
|
|
39
|
+
subprocess.check_call(['cmake', '--build', build_dir, '--config', 'Release'])
|
|
40
|
+
|
|
41
|
+
# copy DLLs to the package
|
|
42
|
+
for arch in arch_names_map:
|
|
43
|
+
for build_dll_dir, package_dll_dir in zip(build_dll_dirs, package_dll_dirs):
|
|
44
|
+
for root, _, files in os.walk(build_dll_dir + build_dirname + arch):
|
|
45
|
+
for file in files:
|
|
46
|
+
if file.endswith('.dll'):
|
|
47
|
+
src_file_path = os.path.join(root, file)
|
|
48
|
+
dest_file_path = os.path.join(package_dll_dir + arch_names_map.get(arch), file)
|
|
49
|
+
os.makedirs(os.path.dirname(dest_file_path), exist_ok=True)
|
|
50
|
+
shutil.copy(src_file_path, dest_file_path)
|
|
51
|
+
super().run()
|
|
52
|
+
|
|
53
|
+
# mark the package is not pure Python code
|
|
54
|
+
class BinaryDistribution(Distribution):
|
|
55
|
+
def is_pure(self):
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
setup(name='injectlib',
|
|
60
|
+
version='0.0.1',
|
|
61
|
+
description='A set of Python modules to inject DLLs into applications for the Microsoft Windows',
|
|
62
|
+
keywords="windows gui .net inject testing test desktop dll wpf qt",
|
|
63
|
+
url="https://github.com/pywinauto/injectlib",
|
|
64
|
+
author='Vasily Ryabov, Ilya Naumov, Boris Galochkin, Alexander Makarov',
|
|
65
|
+
author_email='vasily.v.ryabov@gmail.com',
|
|
66
|
+
long_description="""
|
|
67
|
+
It allows to inject DLls into applications for the Microsoft Windows.
|
|
68
|
+
""",
|
|
69
|
+
platforms=['win32'],
|
|
70
|
+
|
|
71
|
+
packages=["injectlib"],
|
|
72
|
+
package_dir={"": "src"},
|
|
73
|
+
include_package_data=True,
|
|
74
|
+
|
|
75
|
+
license="BSD 3-clause",
|
|
76
|
+
classifiers=[
|
|
77
|
+
'Development Status :: 5 - Production/Stable',
|
|
78
|
+
'Environment :: Console',
|
|
79
|
+
'Intended Audience :: Developers',
|
|
80
|
+
'License :: OSI Approved :: BSD License',
|
|
81
|
+
'Operating System :: Microsoft :: Windows',
|
|
82
|
+
'Programming Language :: Python',
|
|
83
|
+
'Programming Language :: Python :: 3.7',
|
|
84
|
+
'Programming Language :: Python :: 3.8',
|
|
85
|
+
'Programming Language :: Python :: 3.9',
|
|
86
|
+
'Programming Language :: Python :: 3.10',
|
|
87
|
+
'Programming Language :: Python :: 3.11',
|
|
88
|
+
'Programming Language :: Python :: Implementation :: CPython',
|
|
89
|
+
'Topic :: Software Development :: Libraries :: Python Modules',
|
|
90
|
+
'Topic :: Software Development :: Testing',
|
|
91
|
+
],
|
|
92
|
+
install_requires=['pywin32'],
|
|
93
|
+
python_requires='>=3.7',
|
|
94
|
+
cmdclass={'sdist': SdistWithDlls},
|
|
95
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from .injector import Injector
|
|
5
|
+
from .channel import Pipe
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__package__)
|
|
8
|
+
|
|
9
|
+
# backend exit codes enum
|
|
10
|
+
OK = 0
|
|
11
|
+
PARSE_ERROR = 1
|
|
12
|
+
UNSUPPORTED_ACTION = 2
|
|
13
|
+
MISSING_PARAM = 3
|
|
14
|
+
RUNTIME_ERROR = 4
|
|
15
|
+
NOT_FOUND = 5
|
|
16
|
+
UNSUPPORTED_TYPE = 6
|
|
17
|
+
INVALID_VALUE = 7
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class InjectedBaseError(Exception):
|
|
21
|
+
"""Base class for exceptions based on errors returned from injected DLL side"""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class InjectedUnsupportedActionError(InjectedBaseError):
|
|
25
|
+
"""The specified action is not supported"""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class InjectedRuntimeError(InjectedBaseError):
|
|
29
|
+
"""Runtime exception during code execution inside injected target"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class InjectedNotFoundError(InjectedBaseError):
|
|
33
|
+
"""Requested item not found: control element, property, ..."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Singleton(type):
|
|
37
|
+
"""
|
|
38
|
+
Singleton metaclass implementation from StackOverflow
|
|
39
|
+
|
|
40
|
+
http://stackoverflow.com/q/6760685/3648361
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
_instances = {}
|
|
44
|
+
|
|
45
|
+
def __call__(cls, *args, **kwargs):
|
|
46
|
+
if cls not in cls._instances:
|
|
47
|
+
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
|
48
|
+
return cls._instances[cls]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ConnectionManager(object, metaclass=Singleton):
|
|
52
|
+
def __init__(self):
|
|
53
|
+
self._pipes = {}
|
|
54
|
+
|
|
55
|
+
def _get_pipe(self, pid):
|
|
56
|
+
if pid not in self._pipes:
|
|
57
|
+
self._pipes[pid] = self._create_pipe(pid)
|
|
58
|
+
return self._pipes[pid]
|
|
59
|
+
|
|
60
|
+
def _create_pipe(self, pid):
|
|
61
|
+
pipe_name = 'process_{}'.format(pid)
|
|
62
|
+
pipe = Pipe(pipe_name)
|
|
63
|
+
if pipe.connect(n_attempts=1):
|
|
64
|
+
logger.info('Pipe {} found, looks like dll has been injected already'.format(pipe_name))
|
|
65
|
+
return pipe
|
|
66
|
+
else:
|
|
67
|
+
logger.info('Pipe {} not found, injecting dll to the process'.format(pipe_name))
|
|
68
|
+
Injector(pid, 'dotnet', 'bootstrap')
|
|
69
|
+
pipe.connect()
|
|
70
|
+
return pipe
|
|
71
|
+
|
|
72
|
+
def call_action(self, action_name, pid, **params):
|
|
73
|
+
command = {'action': action_name}
|
|
74
|
+
command.update(params)
|
|
75
|
+
|
|
76
|
+
reply = self._get_pipe(pid).transact(json.dumps(command))
|
|
77
|
+
reply = json.loads(reply)
|
|
78
|
+
|
|
79
|
+
reply_code = reply['status_code']
|
|
80
|
+
|
|
81
|
+
if reply_code == UNSUPPORTED_ACTION:
|
|
82
|
+
raise InjectedUnsupportedActionError(reply['message'])
|
|
83
|
+
elif reply_code == RUNTIME_ERROR:
|
|
84
|
+
raise InjectedRuntimeError(reply['message'])
|
|
85
|
+
elif reply_code == NOT_FOUND:
|
|
86
|
+
raise InjectedNotFoundError(reply['message'])
|
|
87
|
+
elif reply_code != OK:
|
|
88
|
+
raise InjectedBaseError()
|
|
89
|
+
|
|
90
|
+
return reply
|
|
@@ -0,0 +1,76 @@
|
|
|
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)
|
|
@@ -0,0 +1,69 @@
|
|
|
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)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import locale
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from . import sysinfo
|
|
6
|
+
from . import cfuncs
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Injector(object):
|
|
10
|
+
"""Class for injections dll"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, pid, backend_name, dll_name, is_unicode=False):
|
|
13
|
+
"""Constructor inject dll (one application - one class instance)."""
|
|
14
|
+
self.is_unicode = is_unicode
|
|
15
|
+
self.pid = pid
|
|
16
|
+
if not sysinfo.is_x64_python() == sysinfo.is64_bitprocess(self.pid):
|
|
17
|
+
raise RuntimeError("Application and Python must be both 32-bit or both 64-bit")
|
|
18
|
+
self.h_process = self._get_process_handle(self.pid)
|
|
19
|
+
|
|
20
|
+
self.dll_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
|
21
|
+
'libs', backend_name,
|
|
22
|
+
'x{}'.format("64" if sysinfo.is64_bitprocess(self.pid) else "86"),
|
|
23
|
+
'{}.dll'.format(dll_name)).encode('utf-16' if self.is_unicode
|
|
24
|
+
else locale.getpreferredencoding())
|
|
25
|
+
self._inject_dll_to_process()
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def _get_process_handle(pid):
|
|
29
|
+
return cfuncs.OpenProcess(cfuncs.PROCESS_ALL_ACCESS, False, pid)
|
|
30
|
+
|
|
31
|
+
def _create_remote_thread_with_timeout(self, proc_address, arg_address, timeout_ms=1000,
|
|
32
|
+
call_err_text="", timeout_err_text=""):
|
|
33
|
+
thread_handle = cfuncs.CreateRemoteThread(self.h_process, None, 0, proc_address, arg_address, 0, None)
|
|
34
|
+
if not thread_handle:
|
|
35
|
+
raise RuntimeError(call_err_text)
|
|
36
|
+
# TODO: add timeout to pywinauto timings and replace
|
|
37
|
+
ret = cfuncs.WaitForSingleObject(thread_handle, ctypes.wintypes.DWORD(timeout_ms))
|
|
38
|
+
if ret == cfuncs.WAIT_TIMEOUT:
|
|
39
|
+
raise TimeoutError(timeout_err_text)
|
|
40
|
+
|
|
41
|
+
def _inject_dll_to_process(self):
|
|
42
|
+
# Get dll path length
|
|
43
|
+
c_dll_path = ctypes.create_unicode_buffer(self.dll_path) \
|
|
44
|
+
if self.is_unicode else ctypes.create_string_buffer(self.dll_path)
|
|
45
|
+
|
|
46
|
+
# Allocate space for DLL path
|
|
47
|
+
arg_address = cfuncs.VirtualAllocEx(self.h_process, 0, ctypes.sizeof(c_dll_path),
|
|
48
|
+
cfuncs.VIRTUAL_MEM, cfuncs.PAGE_READWRITE)
|
|
49
|
+
|
|
50
|
+
# Write DLL path to allocated space
|
|
51
|
+
if not cfuncs.WriteProcessMemory(self.h_process, arg_address,
|
|
52
|
+
ctypes.byref(c_dll_path), ctypes.sizeof(c_dll_path), 0):
|
|
53
|
+
raise AttributeError("Couldn't write data to process memory, check python access.")
|
|
54
|
+
|
|
55
|
+
# Resolve LoadLibraryA(W) Address
|
|
56
|
+
h_kernel32 = cfuncs.GetModuleHandleW(b"kernel32.dll") \
|
|
57
|
+
if self.is_unicode else cfuncs.GetModuleHandleA(b"kernel32.dll")
|
|
58
|
+
h_loadlib = cfuncs.GetProcAddress(h_kernel32, b"LoadLibraryW" if self.is_unicode else b"LoadLibraryA")
|
|
59
|
+
|
|
60
|
+
# Now call createRemoteThread with entry point set to LoadLibraryA(W) and pointer to DLL path as param
|
|
61
|
+
self._create_remote_thread_with_timeout(h_loadlib, arg_address, 1000,
|
|
62
|
+
"Couldn't create remote thread, application and Python must be "
|
|
63
|
+
"both 32-bit or both 64-bit", "Inject time out")
|
|
64
|
+
|
|
65
|
+
def _get_dll_proc_address(self, proc_name):
|
|
66
|
+
lib = cfuncs.LoadLibraryW(self.dll_path) if self.is_unicode else cfuncs.LoadLibraryA(self.dll_path)
|
|
67
|
+
return cfuncs.GetProcAddress(lib, proc_name)
|
|
68
|
+
|
|
69
|
+
def remote_call_void_func(self, func_name):
|
|
70
|
+
proc_address = self._get_dll_proc_address(func_name)
|
|
71
|
+
|
|
72
|
+
if not cfuncs.CreateRemoteThread(self.h_process, None, 0, proc_address, 0, 0, None):
|
|
73
|
+
raise RuntimeError("Couldn't create remote thread, dll not injected, inject and try again!")
|
|
74
|
+
|
|
75
|
+
def remote_call_int_param_func(self, func_name, param):
|
|
76
|
+
# Resolve param type for different applications
|
|
77
|
+
a = ctypes.c_int64(param) if sysinfo.is64_bitprocess(self.pid) else ctypes.c_int32(param)
|
|
78
|
+
|
|
79
|
+
arg_address = cfuncs.VirtualAllocEx(self.h_process, 0, ctypes.sizeof(a),
|
|
80
|
+
cfuncs.VIRTUAL_MEM, cfuncs.PAGE_READWRITE)
|
|
81
|
+
if not cfuncs.WriteProcessMemory(self.h_process, arg_address, ctypes.byref(a), ctypes.sizeof(a), 0):
|
|
82
|
+
raise AttributeError("Couldn't write data to process memory, check python access.")
|
|
83
|
+
|
|
84
|
+
proc_address = self._get_dll_proc_address(func_name)
|
|
85
|
+
|
|
86
|
+
self._create_remote_thread_with_timeout(proc_address, arg_address, 1000,
|
|
87
|
+
"Couldn't create remote thread, dll not injected, "
|
|
88
|
+
"inject it and try again!",
|
|
89
|
+
"{0}(int) function call time out".format(func_name))
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Simple module for checking whether Python and Windows are 32-bit or 64-bit"""
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
5
|
+
import ctypes
|
|
6
|
+
import win32process
|
|
7
|
+
import win32api
|
|
8
|
+
import win32con
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def os_arch():
|
|
12
|
+
architecture_map = {
|
|
13
|
+
'x86': 'x86',
|
|
14
|
+
'i386': 'x86',
|
|
15
|
+
'i486': 'x86',
|
|
16
|
+
'i586': 'x86',
|
|
17
|
+
'i686': 'x86',
|
|
18
|
+
'x64': 'x86_64',
|
|
19
|
+
'AMD64': 'x86_64',
|
|
20
|
+
'amd64': 'x86_64',
|
|
21
|
+
'em64t': 'x86_64',
|
|
22
|
+
'EM64T': 'x86_64',
|
|
23
|
+
'x86_64': 'x86_64',
|
|
24
|
+
'IA64': 'ia64',
|
|
25
|
+
'ia64': 'ia64'
|
|
26
|
+
}
|
|
27
|
+
if sys.platform == 'win32':
|
|
28
|
+
architecture_var = os.environ.get('PROCESSOR_ARCHITEW6432', '')
|
|
29
|
+
if architecture_var == '':
|
|
30
|
+
architecture_var = os.environ.get('PROCESSOR_ARCHITECTURE', '')
|
|
31
|
+
return architecture_map.get(architecture_var, 'Unknown')
|
|
32
|
+
else:
|
|
33
|
+
return architecture_map.get(platform.machine(), '')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def python_bitness():
|
|
37
|
+
return ctypes.sizeof(ctypes.POINTER(ctypes.c_int)) * 8
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def is_x64_python():
|
|
41
|
+
return python_bitness() == 64
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def is_x64_os():
|
|
45
|
+
return os_arch() in ['x86_64', 'ia64']
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def is64_bitprocess(process_id):
|
|
49
|
+
"""Return True if the specified process is a 64-bit one
|
|
50
|
+
|
|
51
|
+
Return False if it is only a 32-bit process running under Wow64.
|
|
52
|
+
Always return False for x86.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
if is_x64_os():
|
|
56
|
+
phndl = win32api.OpenProcess(win32con.MAXIMUM_ALLOWED, 0, process_id)
|
|
57
|
+
if not phndl:
|
|
58
|
+
raise OSError(f'OpenProcess is failed for PID = {process_id}')
|
|
59
|
+
return not win32process.IsWow64Process(phndl)
|
|
60
|
+
|
|
61
|
+
return False
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: injectlib
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A set of Python modules to inject DLLs into applications for the Microsoft Windows
|
|
5
|
+
Home-page: https://github.com/pywinauto/injectlib
|
|
6
|
+
Author: Vasily Ryabov, Ilya Naumov, Boris Galochkin, Alexander Makarov
|
|
7
|
+
Author-email: vasily.v.ryabov@gmail.com
|
|
8
|
+
License: BSD 3-clause
|
|
9
|
+
Keywords: windows gui .net inject testing test desktop dll wpf qt
|
|
10
|
+
Platform: win32
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Topic :: Software Development :: Testing
|
|
25
|
+
Requires-Python: >=3.7
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: pywin32
|
|
28
|
+
Dynamic: author
|
|
29
|
+
Dynamic: author-email
|
|
30
|
+
Dynamic: classifier
|
|
31
|
+
Dynamic: description
|
|
32
|
+
Dynamic: home-page
|
|
33
|
+
Dynamic: keywords
|
|
34
|
+
Dynamic: license
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
Dynamic: platform
|
|
37
|
+
Dynamic: requires-dist
|
|
38
|
+
Dynamic: requires-python
|
|
39
|
+
Dynamic: summary
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
It allows to inject DLls into applications for the Microsoft Windows.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
setup.py
|
|
5
|
+
src/injectlib/__init__.py
|
|
6
|
+
src/injectlib/api.py
|
|
7
|
+
src/injectlib/cfuncs.py
|
|
8
|
+
src/injectlib/channel.py
|
|
9
|
+
src/injectlib/injector.py
|
|
10
|
+
src/injectlib/sysinfo.py
|
|
11
|
+
src/injectlib.egg-info/PKG-INFO
|
|
12
|
+
src/injectlib.egg-info/SOURCES.txt
|
|
13
|
+
src/injectlib.egg-info/dependency_links.txt
|
|
14
|
+
src/injectlib.egg-info/requires.txt
|
|
15
|
+
src/injectlib.egg-info/top_level.txt
|
|
16
|
+
src/injectlib/libs/dotnet/x64/Newtonsoft.Json.dll
|
|
17
|
+
src/injectlib/libs/dotnet/x64/bootstrap.dll
|
|
18
|
+
src/injectlib/libs/dotnet/x64/worker_wpf.dll
|
|
19
|
+
src/injectlib/libs/dotnet/x86/Newtonsoft.Json.dll
|
|
20
|
+
src/injectlib/libs/dotnet/x86/bootstrap.dll
|
|
21
|
+
src/injectlib/libs/dotnet/x86/worker_wpf.dll
|
|
22
|
+
src/injectlib/libs/hook/x64/pywinmsg64.dll
|
|
23
|
+
src/injectlib/libs/hook/x64/pywinmsg64u.dll
|
|
24
|
+
src/injectlib/libs/hook/x86/pywinmsg32.dll
|
|
25
|
+
src/injectlib/libs/hook/x86/pywinmsg32u.dll
|
|
26
|
+
src/injectlib/libs/qt/x64/qt_srv.dll
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pywin32
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
injectlib
|