levistone 0.6.1__1-cp310-cp310-win_amd64.whl → 0.9.3__1-cp310-cp310-win_amd64.whl
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.
Potentially problematic release.
This version of levistone might be problematic. Click here for more details.
- endstone/__init__.py +16 -2
- endstone/_internal/bootstrap/__init__.py +6 -1
- endstone/_internal/bootstrap/base.py +56 -52
- endstone/_internal/bootstrap/linux.py +24 -5
- endstone/_internal/bootstrap/windows.py +19 -167
- endstone/_internal/endstone_python.cp310-win_amd64.pyd +0 -0
- endstone/_internal/endstone_python.pyi +968 -451
- endstone/_internal/metrics.py +2 -2
- endstone/_internal/plugin_loader.py +11 -4
- endstone/_internal/version.py +2 -2
- endstone/command.py +8 -2
- endstone/config/endstone.toml +4 -0
- endstone/enchantments.py +3 -0
- endstone/event.py +24 -0
- endstone/form.py +15 -1
- endstone/inventory.py +20 -2
- endstone/map.py +3 -0
- endstone/plugin.py +7 -2
- endstone_runtime.dll +0 -0
- endstone_runtime.pdb +0 -0
- {levistone-0.6.1.dist-info → levistone-0.9.3.dist-info}/METADATA +4 -2
- levistone-0.9.3.dist-info/RECORD +40 -0
- {levistone-0.6.1.dist-info → levistone-0.9.3.dist-info}/WHEEL +1 -1
- levistone-0.9.3.dist-info/licenses/LICENSE +201 -0
- manifest.json +1 -1
- endstone/_internal/endstone_python.pyd +0 -0
- endstone/network.py +0 -3
- levistone-0.6.1.dist-info/RECORD +0 -37
- {levistone-0.6.1.dist-info → levistone-0.9.3.dist-info}/entry_points.txt +0 -0
- {levistone-0.6.1.dist-info → levistone-0.9.3.dist-info}/top_level.txt +0 -0
endstone/__init__.py
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
|
-
from endstone._internal.endstone_python import
|
|
1
|
+
from endstone._internal.endstone_python import (
|
|
2
|
+
ColorFormat,
|
|
3
|
+
EnchantmentRegistry,
|
|
4
|
+
GameMode,
|
|
5
|
+
ItemRegistry,
|
|
6
|
+
Logger,
|
|
7
|
+
NamespacedKey,
|
|
8
|
+
OfflinePlayer,
|
|
9
|
+
Player,
|
|
10
|
+
Server,
|
|
11
|
+
Skin,
|
|
12
|
+
)
|
|
2
13
|
from endstone._internal.version import __version__
|
|
3
14
|
|
|
4
|
-
__minecraft_version__ = "1.21.
|
|
15
|
+
__minecraft_version__ = "1.21.93"
|
|
5
16
|
|
|
6
17
|
__all__ = [
|
|
7
18
|
"__version__",
|
|
8
19
|
"__minecraft_version__",
|
|
9
20
|
"ColorFormat",
|
|
21
|
+
"EnchantmentRegistry",
|
|
10
22
|
"GameMode",
|
|
23
|
+
"ItemRegistry",
|
|
11
24
|
"Logger",
|
|
25
|
+
"NamespacedKey",
|
|
12
26
|
"OfflinePlayer",
|
|
13
27
|
"Player",
|
|
14
28
|
"Server",
|
|
@@ -2,6 +2,7 @@ import functools
|
|
|
2
2
|
import logging
|
|
3
3
|
import platform
|
|
4
4
|
import sys
|
|
5
|
+
import time
|
|
5
6
|
|
|
6
7
|
import click
|
|
7
8
|
|
|
@@ -50,7 +51,7 @@ def catch_exceptions(func):
|
|
|
50
51
|
@click.option(
|
|
51
52
|
"-r",
|
|
52
53
|
"--remote",
|
|
53
|
-
default="https://raw.githubusercontent.com/EndstoneMC/bedrock-server-data/
|
|
54
|
+
default="https://raw.githubusercontent.com/EndstoneMC/bedrock-server-data/v2",
|
|
54
55
|
help="The remote URL to retrieve bedrock server data from.",
|
|
55
56
|
)
|
|
56
57
|
@click.version_option(__version__)
|
|
@@ -71,4 +72,8 @@ def cli(server_folder: str, no_confirm: bool, remote: str) -> None:
|
|
|
71
72
|
|
|
72
73
|
bootstrap = cls(server_folder=server_folder, no_confirm=no_confirm, remote=remote)
|
|
73
74
|
exit_code = bootstrap.run()
|
|
75
|
+
if exit_code != 0:
|
|
76
|
+
logger.error(f"Server exited with non-zero code {exit_code}.")
|
|
77
|
+
time.sleep(2)
|
|
78
|
+
|
|
74
79
|
sys.exit(exit_code)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import errno
|
|
2
|
+
import fnmatch
|
|
2
3
|
import hashlib
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
5
6
|
import platform
|
|
7
|
+
import shutil
|
|
6
8
|
import subprocess
|
|
7
9
|
import sys
|
|
8
10
|
import tempfile
|
|
@@ -11,12 +13,13 @@ from pathlib import Path
|
|
|
11
13
|
from typing import Union
|
|
12
14
|
|
|
13
15
|
import click
|
|
16
|
+
import importlib_resources
|
|
14
17
|
import requests
|
|
18
|
+
import sentry_crashpad
|
|
15
19
|
from packaging.version import Version
|
|
16
20
|
from rich.progress import BarColumn, DownloadColumn, Progress, TextColumn, TimeRemainingColumn
|
|
17
21
|
|
|
18
22
|
from endstone import __minecraft_version__ as minecraft_version
|
|
19
|
-
from endstone import __version__ as endstone_version
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
class Bootstrap:
|
|
@@ -47,13 +50,17 @@ class Bootstrap:
|
|
|
47
50
|
def executable_path(self) -> Path:
|
|
48
51
|
return self.server_path / self.executable_filename
|
|
49
52
|
|
|
53
|
+
@property
|
|
54
|
+
def config_path(self) -> Path:
|
|
55
|
+
return self.server_path / "endstone.toml"
|
|
56
|
+
|
|
50
57
|
@property
|
|
51
58
|
def plugin_path(self) -> Path:
|
|
52
59
|
return self.server_path / "plugins"
|
|
53
60
|
|
|
54
61
|
@property
|
|
55
62
|
def user_agent(self) -> str:
|
|
56
|
-
return
|
|
63
|
+
return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36"
|
|
57
64
|
|
|
58
65
|
def _validate(self) -> None:
|
|
59
66
|
if platform.system().lower() != self.target_system:
|
|
@@ -67,22 +74,23 @@ class Bootstrap:
|
|
|
67
74
|
dst = Path(dst)
|
|
68
75
|
|
|
69
76
|
self._logger.info("Loading index from the remote server...")
|
|
70
|
-
|
|
77
|
+
channel = "preview" if Version(minecraft_version).is_prerelease else "release"
|
|
78
|
+
metadata_url = "/".join([self._remote, channel, minecraft_version, "metadata.json"])
|
|
79
|
+
response = requests.get(metadata_url, timeout=10)
|
|
71
80
|
response.raise_for_status()
|
|
72
|
-
|
|
81
|
+
metadata = response.json()
|
|
73
82
|
|
|
74
|
-
if minecraft_version
|
|
75
|
-
raise ValueError(f"Version
|
|
83
|
+
if minecraft_version != metadata["version"]:
|
|
84
|
+
raise ValueError(f"Version mismatch, expect: {minecraft_version}, actual: {metadata['version']}")
|
|
76
85
|
|
|
77
86
|
should_modify_server_properties = True
|
|
78
87
|
|
|
79
88
|
with tempfile.TemporaryFile(dir=dst) as f:
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
url = metadata["binary"][self.target_system.lower()]["url"]
|
|
90
|
+
self._logger.info(f"Downloading server from {url}...")
|
|
82
91
|
response = requests.get(url, stream=True, headers={"User-Agent": self.user_agent})
|
|
83
92
|
response.raise_for_status()
|
|
84
93
|
total_size = int(response.headers.get("Content-Length", 0))
|
|
85
|
-
self._logger.info(f"Downloading server from {url}...")
|
|
86
94
|
m = hashlib.sha256()
|
|
87
95
|
|
|
88
96
|
with Progress(
|
|
@@ -98,17 +106,28 @@ class Bootstrap:
|
|
|
98
106
|
m.update(data)
|
|
99
107
|
|
|
100
108
|
self._logger.info("Download complete. Verifying integrity...")
|
|
101
|
-
if m.hexdigest() != metadata["sha256"]:
|
|
109
|
+
if m.hexdigest() != metadata["binary"][self.target_system.lower()]["sha256"]:
|
|
102
110
|
raise ValueError("SHA256 mismatch: the downloaded file may be corrupted or tampered with.")
|
|
103
111
|
|
|
104
112
|
self._logger.info(f"Integrity check passed. Extracting to {dst}...")
|
|
105
113
|
dst.mkdir(parents=True, exist_ok=True)
|
|
114
|
+
override_patterns = [
|
|
115
|
+
self.executable_filename,
|
|
116
|
+
"behavior_packs/*",
|
|
117
|
+
"definitions/*",
|
|
118
|
+
"resource_packs/*",
|
|
119
|
+
"bedrock_server_how_to.html",
|
|
120
|
+
"profanity_filter.wlist",
|
|
121
|
+
"release-notes.txt",
|
|
122
|
+
]
|
|
106
123
|
with zipfile.ZipFile(f) as zip_ref:
|
|
107
124
|
for file in zip_ref.namelist():
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
125
|
+
dest_path = dst / file
|
|
126
|
+
if dest_path.exists():
|
|
127
|
+
if not any(fnmatch.fnmatch(file, pattern) for pattern in override_patterns):
|
|
128
|
+
should_modify_server_properties = False
|
|
129
|
+
self._logger.info(f"{dest_path} already exists, skipping.")
|
|
130
|
+
continue
|
|
112
131
|
|
|
113
132
|
zip_ref.extract(file, dst)
|
|
114
133
|
|
|
@@ -135,6 +154,13 @@ class Bootstrap:
|
|
|
135
154
|
|
|
136
155
|
def _prepare(self) -> None:
|
|
137
156
|
self.plugin_path.mkdir(parents=True, exist_ok=True)
|
|
157
|
+
shutil.copytree(
|
|
158
|
+
Path(sentry_crashpad._get_executable("crashpad_handler")).parent, self.server_path, dirs_exist_ok=True
|
|
159
|
+
)
|
|
160
|
+
if not self.config_path.exists():
|
|
161
|
+
ref = importlib_resources.files("endstone") / "config" / "endstone.toml"
|
|
162
|
+
with importlib_resources.as_file(ref) as path:
|
|
163
|
+
shutil.copy(path, self.config_path)
|
|
138
164
|
|
|
139
165
|
def _install(self) -> None:
|
|
140
166
|
"""
|
|
@@ -199,8 +225,7 @@ class Bootstrap:
|
|
|
199
225
|
self._install()
|
|
200
226
|
self._validate()
|
|
201
227
|
self._prepare()
|
|
202
|
-
self.
|
|
203
|
-
return self._wait_for_server()
|
|
228
|
+
return self._run()
|
|
204
229
|
|
|
205
230
|
@property
|
|
206
231
|
def _endstone_runtime_filename(self) -> str:
|
|
@@ -211,48 +236,27 @@ class Bootstrap:
|
|
|
211
236
|
p = Path(__file__).parent.parent / self._endstone_runtime_filename
|
|
212
237
|
return p.resolve().absolute()
|
|
213
238
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
This method initializes a subprocess.Popen object for the server executable. It sets up the necessary
|
|
219
|
-
buffers and encodings for the process and specifies the working directory.
|
|
220
|
-
|
|
221
|
-
Args:
|
|
222
|
-
*args: Variable length argument list.
|
|
223
|
-
**kwargs: Arbitrary keyword arguments.
|
|
224
|
-
|
|
225
|
-
"""
|
|
226
|
-
env = kwargs.pop("env", os.environ.copy())
|
|
239
|
+
@property
|
|
240
|
+
def _endstone_runtime_env(self) -> dict[str, str]:
|
|
241
|
+
env = os.environ.copy()
|
|
227
242
|
env["PATH"] = os.pathsep.join(sys.path)
|
|
228
243
|
env["PYTHONPATH"] = os.pathsep.join(sys.path)
|
|
229
244
|
env["PYTHONIOENCODING"] = "UTF-8"
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
stdin=sys.stdin,
|
|
233
|
-
stdout=sys.stdout,
|
|
234
|
-
stderr=subprocess.STDOUT,
|
|
235
|
-
text=True,
|
|
236
|
-
encoding="utf-8",
|
|
237
|
-
cwd=str(self.server_path.absolute()),
|
|
238
|
-
env=env,
|
|
239
|
-
*args,
|
|
240
|
-
**kwargs,
|
|
241
|
-
)
|
|
245
|
+
env["ENDSTONE_PYTHON_EXECUTABLE"] = sys.executable
|
|
246
|
+
return env
|
|
242
247
|
|
|
243
|
-
def
|
|
248
|
+
def _run(self, *args, **kwargs) -> int:
|
|
244
249
|
"""
|
|
245
|
-
|
|
250
|
+
Runs the server and returns its exit code.
|
|
246
251
|
|
|
247
|
-
This method blocks until the server process
|
|
248
|
-
|
|
249
|
-
|
|
252
|
+
This method blocks until the server process terminates. It returns the exit code of the process, which can be
|
|
253
|
+
used to determine if the server shut down successfully or if there were errors.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
*args: Variable length argument list.
|
|
257
|
+
**kwargs: Arbitrary keyword arguments.
|
|
250
258
|
|
|
251
259
|
Returns:
|
|
252
|
-
int: The exit code of the server process.
|
|
260
|
+
int: The exit code of the server process.
|
|
253
261
|
"""
|
|
254
|
-
|
|
255
|
-
if self._process is None:
|
|
256
|
-
return -1
|
|
257
|
-
|
|
258
|
-
return self._process.wait()
|
|
262
|
+
raise NotImplementedError
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import ctypes.util
|
|
2
2
|
import os
|
|
3
3
|
import stat
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
4
6
|
from pathlib import Path
|
|
5
7
|
|
|
6
8
|
from endstone._internal.bootstrap.base import Bootstrap
|
|
@@ -23,16 +25,33 @@ class LinuxBootstrap(Bootstrap):
|
|
|
23
25
|
def _endstone_runtime_filename(self) -> str:
|
|
24
26
|
return "libendstone_runtime.so"
|
|
25
27
|
|
|
28
|
+
@property
|
|
29
|
+
def _endstone_runtime_env(self) -> dict[str, str]:
|
|
30
|
+
env = super()._endstone_runtime_env
|
|
31
|
+
env["LD_PRELOAD"] = str(self._endstone_runtime_path.absolute())
|
|
32
|
+
env["LD_LIBRARY_PATH"] = str(self._linked_libpython_path.parent.absolute())
|
|
33
|
+
return env
|
|
34
|
+
|
|
26
35
|
def _prepare(self) -> None:
|
|
27
36
|
super()._prepare()
|
|
28
37
|
st = os.stat(self.executable_path)
|
|
29
38
|
os.chmod(self.executable_path, st.st_mode | stat.S_IEXEC)
|
|
39
|
+
os.chmod(self.server_path / "crashpad_handler", st.st_mode | stat.S_IEXEC)
|
|
30
40
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
def _run(self, *args, **kwargs) -> int:
|
|
42
|
+
process = subprocess.Popen(
|
|
43
|
+
[str(self.executable_path.absolute())],
|
|
44
|
+
stdin=sys.stdin,
|
|
45
|
+
stdout=sys.stdout,
|
|
46
|
+
stderr=subprocess.STDOUT,
|
|
47
|
+
text=True,
|
|
48
|
+
encoding="utf-8",
|
|
49
|
+
cwd=str(self.server_path.absolute()),
|
|
50
|
+
env=self._endstone_runtime_env,
|
|
51
|
+
*args,
|
|
52
|
+
**kwargs,
|
|
53
|
+
)
|
|
54
|
+
return process.wait()
|
|
36
55
|
|
|
37
56
|
@property
|
|
38
57
|
def _linked_libpython_path(self) -> Path:
|
|
@@ -1,86 +1,9 @@
|
|
|
1
1
|
import ctypes
|
|
2
2
|
import os
|
|
3
3
|
import subprocess
|
|
4
|
-
from ctypes import get_last_error
|
|
5
|
-
from ctypes.wintypes import (
|
|
6
|
-
BOOL,
|
|
7
|
-
DWORD,
|
|
8
|
-
HANDLE,
|
|
9
|
-
HMODULE,
|
|
10
|
-
LPCSTR,
|
|
11
|
-
LPCVOID,
|
|
12
|
-
LPCWSTR,
|
|
13
|
-
LPDWORD,
|
|
14
|
-
LPVOID,
|
|
15
|
-
)
|
|
16
4
|
|
|
17
5
|
from endstone._internal.bootstrap.base import Bootstrap
|
|
18
|
-
|
|
19
|
-
SIZE_T = ctypes.c_size_t
|
|
20
|
-
PSIZE_T = ctypes.POINTER(SIZE_T)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class THREADENTRY32(ctypes.Structure):
|
|
24
|
-
_fields_ = [
|
|
25
|
-
("dwSize", DWORD),
|
|
26
|
-
("cntUsage", DWORD),
|
|
27
|
-
("th32ThreadID", DWORD),
|
|
28
|
-
("th32OwnerProcessID", DWORD),
|
|
29
|
-
("tpBasePri", DWORD),
|
|
30
|
-
("tpDeltaPri", DWORD),
|
|
31
|
-
("dwFlags", DWORD),
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
# Kernel32 Functions
|
|
36
|
-
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
|
|
37
|
-
|
|
38
|
-
# Constants
|
|
39
|
-
CREATE_SUSPENDED = 0x00000004 # https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
|
|
40
|
-
MAX_PATH = 260 # https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
|
|
41
|
-
MEM_COMMIT = 0x00001000 # https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
|
|
42
|
-
MEM_RESERVE = 0x00002000 # https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
|
|
43
|
-
PAGE_READWRITE = 0x04 # https://learn.microsoft.com/en-us/windows/win32/memory/memory-protection-constants
|
|
44
|
-
INFINITE = 0xFFFFFFFF
|
|
45
|
-
TH32CS_SNAPTHREAD = 0x00000004
|
|
46
|
-
THREAD_ALL_ACCESS = 0x000F0000 | 0x00100000 | 0xFFFF
|
|
47
|
-
INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value
|
|
48
|
-
DONT_RESOLVE_DLL_REFERENCES = 0x00000001
|
|
49
|
-
|
|
50
|
-
# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex
|
|
51
|
-
VirtualAllocEx = kernel32.VirtualAllocEx
|
|
52
|
-
VirtualAllocEx.restype = LPVOID
|
|
53
|
-
VirtualAllocEx.argtypes = (HANDLE, LPVOID, SIZE_T, DWORD, DWORD)
|
|
54
|
-
|
|
55
|
-
# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory
|
|
56
|
-
WriteProcessMemory = kernel32.WriteProcessMemory
|
|
57
|
-
WriteProcessMemory.restype = BOOL
|
|
58
|
-
WriteProcessMemory.argtypes = (HANDLE, LPVOID, LPCVOID, SIZE_T, PSIZE_T)
|
|
59
|
-
|
|
60
|
-
# https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread
|
|
61
|
-
CreateRemoteThread = kernel32.CreateRemoteThread
|
|
62
|
-
CreateRemoteThread.restype = HANDLE
|
|
63
|
-
CreateRemoteThread.argtypes = (HANDLE, LPVOID, SIZE_T, LPVOID, LPVOID, DWORD, LPDWORD)
|
|
64
|
-
|
|
65
|
-
# https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodethread
|
|
66
|
-
GetExitCodeThread = kernel32.GetExitCodeThread
|
|
67
|
-
GetExitCodeThread.restype = BOOL
|
|
68
|
-
GetExitCodeThread.argtypes = (HANDLE, LPDWORD)
|
|
69
|
-
|
|
70
|
-
# https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew
|
|
71
|
-
GetModuleHandle = kernel32.GetModuleHandleW
|
|
72
|
-
GetModuleHandle.restype = HMODULE
|
|
73
|
-
GetModuleHandle.argtypes = (LPCWSTR,)
|
|
74
|
-
|
|
75
|
-
# https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress
|
|
76
|
-
GetProcAddress = kernel32.GetProcAddress
|
|
77
|
-
GetProcAddress.restype = LPVOID
|
|
78
|
-
GetProcAddress.argtypes = (HMODULE, LPCSTR)
|
|
79
|
-
|
|
80
|
-
# https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw
|
|
81
|
-
LoadLibraryExW = kernel32.LoadLibraryExW
|
|
82
|
-
LoadLibraryExW.restype = HMODULE
|
|
83
|
-
LoadLibraryExW.argtypes = (LPCWSTR, HANDLE, DWORD)
|
|
6
|
+
from endstone._internal.winext import start_process_with_dll
|
|
84
7
|
|
|
85
8
|
|
|
86
9
|
class WindowsBootstrap(Bootstrap):
|
|
@@ -100,6 +23,18 @@ class WindowsBootstrap(Bootstrap):
|
|
|
100
23
|
def _endstone_runtime_filename(self) -> str:
|
|
101
24
|
return "endstone_runtime_loader.dll"
|
|
102
25
|
|
|
26
|
+
@property
|
|
27
|
+
def _endstone_runtime_env(self) -> dict[str, str]:
|
|
28
|
+
env = super()._endstone_runtime_env
|
|
29
|
+
symbol_path = env.get("_NT_SYMBOL_PATH", "")
|
|
30
|
+
symbol_path_list = symbol_path.split(os.pathsep)
|
|
31
|
+
symbol_path_list = [
|
|
32
|
+
str(self._endstone_runtime_path.parent.absolute()),
|
|
33
|
+
str(self.plugin_path.absolute()),
|
|
34
|
+
] + symbol_path_list
|
|
35
|
+
env["_NT_SYMBOL_PATH"] = os.pathsep.join(symbol_path_list)
|
|
36
|
+
return env
|
|
37
|
+
|
|
103
38
|
def _add_loopback_exemption(self) -> bool:
|
|
104
39
|
sid = "S-1-15-2-1958404141-86561845-1752920682-3514627264-368642714-62675701-733520436"
|
|
105
40
|
ret = subprocess.run(
|
|
@@ -113,95 +48,12 @@ class WindowsBootstrap(Bootstrap):
|
|
|
113
48
|
else:
|
|
114
49
|
return True
|
|
115
50
|
|
|
116
|
-
def
|
|
51
|
+
def _run(self, *args, **kwargs) -> int:
|
|
117
52
|
self._add_loopback_exemption()
|
|
118
53
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
str(self._endstone_runtime_path.parent.absolute()),
|
|
125
|
-
str(self.plugin_path.absolute()),
|
|
126
|
-
] + symbol_path_list
|
|
127
|
-
env["_NT_SYMBOL_PATH"] = os.pathsep.join(symbol_path_list)
|
|
128
|
-
|
|
129
|
-
# Create the process is a suspended state
|
|
130
|
-
super()._create_process(creationflags=CREATE_SUSPENDED, env=env)
|
|
131
|
-
handle_proc = int(self._process._handle)
|
|
132
|
-
lib_path = str(self._endstone_runtime_path.absolute())
|
|
133
|
-
|
|
134
|
-
# Validate dll
|
|
135
|
-
dll = kernel32.LoadLibraryExW(lib_path, None, DONT_RESOLVE_DLL_REFERENCES)
|
|
136
|
-
if not dll:
|
|
137
|
-
raise ValueError(f"LoadLibraryExW failed with error {get_last_error()}.")
|
|
138
|
-
|
|
139
|
-
# Allocate memory for lib_path
|
|
140
|
-
address = kernel32.VirtualAllocEx(
|
|
141
|
-
handle_proc, # hProcess
|
|
142
|
-
0, # lpAddress
|
|
143
|
-
MAX_PATH * 2 + 1, # dwSize
|
|
144
|
-
DWORD(MEM_COMMIT | MEM_RESERVE), # flAllocationType
|
|
145
|
-
DWORD(PAGE_READWRITE), # flProtect
|
|
146
|
-
)
|
|
147
|
-
if not address:
|
|
148
|
-
raise ValueError(f"VirtualAllocEx failed with error {get_last_error()}.")
|
|
149
|
-
|
|
150
|
-
# Write lib_path into the allocated memory
|
|
151
|
-
size_written = SIZE_T(0)
|
|
152
|
-
result = kernel32.WriteProcessMemory(
|
|
153
|
-
handle_proc, address, lib_path, len(lib_path) * 2 + 1, ctypes.byref(size_written)
|
|
54
|
+
return start_process_with_dll(
|
|
55
|
+
str(self.executable_path.absolute()),
|
|
56
|
+
str(self._endstone_runtime_path.absolute()),
|
|
57
|
+
cwd=str(self.server_path.absolute()),
|
|
58
|
+
env=self._endstone_runtime_env,
|
|
154
59
|
)
|
|
155
|
-
if not result:
|
|
156
|
-
raise ValueError(f"WriteProcessMemory failed with error {get_last_error()}.")
|
|
157
|
-
|
|
158
|
-
# Get module handle of kernel32
|
|
159
|
-
handle_kernel32 = kernel32.GetModuleHandleW("kernel32.dll")
|
|
160
|
-
if not handle_kernel32:
|
|
161
|
-
raise ValueError(f"GetModuleHandleW failed with error {get_last_error()}.")
|
|
162
|
-
|
|
163
|
-
# Get address of LoadLibraryW
|
|
164
|
-
load_library = kernel32.GetProcAddress(handle_kernel32, b"LoadLibraryW")
|
|
165
|
-
if not load_library:
|
|
166
|
-
raise ValueError(f"GetProcAddress failed with error {get_last_error()}.")
|
|
167
|
-
|
|
168
|
-
# Start a new thread in the process, starting at LoadLibraryW with address as argument
|
|
169
|
-
remote_thread = kernel32.CreateRemoteThread(handle_proc, None, 0, load_library, address, 0, None)
|
|
170
|
-
if not remote_thread:
|
|
171
|
-
raise ValueError(f"CreateRemoteThread failed with error {get_last_error()}.")
|
|
172
|
-
|
|
173
|
-
# Wait for the remote thread to finish
|
|
174
|
-
if kernel32.WaitForSingleObject(remote_thread, INFINITE) == 0xFFFFFFFF:
|
|
175
|
-
raise ValueError(f"WaitForSingleObject failed with error {get_last_error()}.")
|
|
176
|
-
|
|
177
|
-
# Check dll load result
|
|
178
|
-
exit_code = DWORD()
|
|
179
|
-
result = kernel32.GetExitCodeThread(remote_thread, ctypes.byref(exit_code))
|
|
180
|
-
if result == 0:
|
|
181
|
-
raise ValueError(f"LoadLibrary failed with thread exit code: {exit_code.value}")
|
|
182
|
-
|
|
183
|
-
# Reopen the handle to process thread (which was closed by subprocess.Popen)
|
|
184
|
-
snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)
|
|
185
|
-
if snapshot == INVALID_HANDLE_VALUE:
|
|
186
|
-
raise ValueError(f"CreateToolhelp32Snapshot failed with error {get_last_error()}.")
|
|
187
|
-
|
|
188
|
-
thread_entry = THREADENTRY32()
|
|
189
|
-
thread_entry.dwSize = ctypes.sizeof(thread_entry)
|
|
190
|
-
success = kernel32.Thread32First(snapshot, ctypes.byref(thread_entry))
|
|
191
|
-
if not success:
|
|
192
|
-
raise ValueError(f"Thread32First failed with error {get_last_error()}.")
|
|
193
|
-
|
|
194
|
-
handle_thread = None
|
|
195
|
-
while success:
|
|
196
|
-
if thread_entry.th32OwnerProcessID == self._process.pid:
|
|
197
|
-
handle_thread = kernel32.OpenThread(THREAD_ALL_ACCESS, None, thread_entry.th32ThreadID)
|
|
198
|
-
break
|
|
199
|
-
|
|
200
|
-
success = kernel32.Thread32Next(snapshot, ctypes.byref(thread_entry))
|
|
201
|
-
|
|
202
|
-
if handle_thread is None:
|
|
203
|
-
raise ValueError(f"OpenThread failed with error {get_last_error()}.")
|
|
204
|
-
|
|
205
|
-
# Resume main thread execution
|
|
206
|
-
kernel32.ResumeThread(handle_thread)
|
|
207
|
-
kernel32.CloseHandle(handle_thread)
|
|
Binary file
|