h4-debug 0.2.0__tar.gz → 0.2.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.
- {h4_debug-0.2.0 → h4_debug-0.2.1}/PKG-INFO +2 -1
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug/win_debugger.py +97 -1
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug.egg-info/PKG-INFO +2 -1
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug.egg-info/requires.txt +1 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/pyproject.toml +2 -1
- {h4_debug-0.2.0 → h4_debug-0.2.1}/README.md +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug/__init__.py +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug/cli.py +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug/handlers.py +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug/interceptor.py +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug/node_interceptor.js +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug/server.py +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug/static/dashboard.css +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug/static/dashboard.html +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug/static/dashboard.js +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug.egg-info/SOURCES.txt +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug.egg-info/dependency_links.txt +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug.egg-info/entry_points.txt +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/h4_debug.egg-info/top_level.txt +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: h4-debug
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Advanced application proxy debugger and F12-style web console
|
|
5
5
|
Author-email: h4 <h4@example.com>
|
|
6
6
|
Requires-Python: >=3.8
|
|
@@ -9,6 +9,7 @@ Requires-Dist: fastapi>=0.100
|
|
|
9
9
|
Requires-Dist: uvicorn[standard]>=0.20
|
|
10
10
|
Requires-Dist: websockets>=11.0
|
|
11
11
|
Requires-Dist: Pillow>=10.0.0
|
|
12
|
+
Requires-Dist: psutil>=5.9.0
|
|
12
13
|
|
|
13
14
|
# h4-debug
|
|
14
15
|
|
|
@@ -157,24 +157,110 @@ def read_process_memory(hProcess, address, size):
|
|
|
157
157
|
return buffer.raw[:bytesRead.value]
|
|
158
158
|
return b""
|
|
159
159
|
|
|
160
|
+
def psutil_daemon(pid, client, stop_event):
|
|
161
|
+
import psutil
|
|
162
|
+
import threading
|
|
163
|
+
try:
|
|
164
|
+
proc = psutil.Process(pid)
|
|
165
|
+
except psutil.NoSuchProcess:
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
seen_files = set()
|
|
169
|
+
seen_conns = set()
|
|
170
|
+
|
|
171
|
+
while not stop_event.is_set():
|
|
172
|
+
try:
|
|
173
|
+
# Poll network
|
|
174
|
+
conns = proc.connections(kind='all')
|
|
175
|
+
for c in conns:
|
|
176
|
+
conn_id = f"{c.laddr}-{c.raddr}-{c.status}"
|
|
177
|
+
if conn_id not in seen_conns:
|
|
178
|
+
seen_conns.add(conn_id)
|
|
179
|
+
addr = str(c.raddr) if c.raddr else str(c.laddr)
|
|
180
|
+
client.send("Network", "connect", {"address": f"{c.status} {addr}"})
|
|
181
|
+
|
|
182
|
+
# Poll files
|
|
183
|
+
files = proc.open_files()
|
|
184
|
+
for f in files:
|
|
185
|
+
if f.path not in seen_files:
|
|
186
|
+
seen_files.add(f.path)
|
|
187
|
+
client.send("Disk", "open", {"file": f.path, "mode": f.mode if hasattr(f, 'mode') else 'r'})
|
|
188
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
189
|
+
break
|
|
190
|
+
except Exception:
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
time.sleep(0.1)
|
|
194
|
+
|
|
195
|
+
class SECURITY_ATTRIBUTES(ctypes.Structure):
|
|
196
|
+
_fields_ = [
|
|
197
|
+
("nLength", wintypes.DWORD),
|
|
198
|
+
("lpSecurityDescriptor", ctypes.c_void_p),
|
|
199
|
+
("bInheritHandle", wintypes.BOOL),
|
|
200
|
+
]
|
|
201
|
+
|
|
160
202
|
def debug_process(command, client):
|
|
161
203
|
si = STARTUPINFO()
|
|
162
204
|
si.cb = ctypes.sizeof(si)
|
|
163
205
|
pi = PROCESS_INFORMATION()
|
|
164
206
|
|
|
207
|
+
# Create Pipe for stdout/stderr
|
|
208
|
+
hReadPipe = wintypes.HANDLE()
|
|
209
|
+
hWritePipe = wintypes.HANDLE()
|
|
210
|
+
sa = SECURITY_ATTRIBUTES()
|
|
211
|
+
sa.nLength = ctypes.sizeof(SECURITY_ATTRIBUTES)
|
|
212
|
+
sa.bInheritHandle = True
|
|
213
|
+
sa.lpSecurityDescriptor = None
|
|
214
|
+
|
|
215
|
+
if kernel32.CreatePipe(ctypes.byref(hReadPipe), ctypes.byref(hWritePipe), ctypes.byref(sa), 0):
|
|
216
|
+
# Ensure the read handle to the pipe is not inherited
|
|
217
|
+
kernel32.SetHandleInformation(hReadPipe, 1, 0) # HANDLE_FLAG_INHERIT = 1
|
|
218
|
+
|
|
219
|
+
si.hStdError = hWritePipe
|
|
220
|
+
si.hStdOutput = hWritePipe
|
|
221
|
+
si.dwFlags |= 0x00000100 # STARTF_USESTDHANDLES
|
|
222
|
+
|
|
165
223
|
creation_flags = DEBUG_PROCESS
|
|
166
224
|
|
|
167
225
|
# Convert command list to string for Windows
|
|
168
226
|
cmd_str = " ".join(f'"{c}"' if " " in c else c for c in command)
|
|
169
227
|
|
|
170
228
|
if not kernel32.CreateProcessW(
|
|
171
|
-
None, ctypes.c_wchar_p(cmd_str), None, None,
|
|
229
|
+
None, ctypes.c_wchar_p(cmd_str), None, None, True, # bInheritHandles=True
|
|
172
230
|
creation_flags, None, None, ctypes.byref(si), ctypes.byref(pi)):
|
|
173
231
|
client.send("System", "error", {"text": f"Failed to CreateProcess: {ctypes.GetLastError()}"})
|
|
232
|
+
if hWritePipe:
|
|
233
|
+
kernel32.CloseHandle(hReadPipe)
|
|
234
|
+
kernel32.CloseHandle(hWritePipe)
|
|
174
235
|
return
|
|
175
236
|
|
|
237
|
+
# Close our write end of the pipe so the read thread unblocks when the process exits
|
|
238
|
+
if hWritePipe:
|
|
239
|
+
kernel32.CloseHandle(hWritePipe)
|
|
240
|
+
|
|
241
|
+
def stream_pipe(handle, client):
|
|
242
|
+
buffer = ctypes.create_string_buffer(4096)
|
|
243
|
+
bytes_read = wintypes.DWORD()
|
|
244
|
+
while True:
|
|
245
|
+
if not kernel32.ReadFile(handle, buffer, 4096, ctypes.byref(bytes_read), None) or bytes_read.value == 0:
|
|
246
|
+
break
|
|
247
|
+
text = buffer.raw[:bytes_read.value].decode('utf-8', 'replace').strip('\r\n\x00')
|
|
248
|
+
if text:
|
|
249
|
+
for line in text.splitlines():
|
|
250
|
+
client.send("Console", "stdout", {"text": line})
|
|
251
|
+
kernel32.CloseHandle(handle)
|
|
252
|
+
|
|
253
|
+
pipe_thread = None
|
|
254
|
+
if hReadPipe:
|
|
255
|
+
pipe_thread = threading.Thread(target=stream_pipe, args=(hReadPipe, client), daemon=True)
|
|
256
|
+
pipe_thread.start()
|
|
257
|
+
|
|
176
258
|
process_handle = None
|
|
177
259
|
debug_event = DEBUG_EVENT()
|
|
260
|
+
|
|
261
|
+
import threading
|
|
262
|
+
daemon_stop_event = threading.Event()
|
|
263
|
+
daemon_thread = None
|
|
178
264
|
|
|
179
265
|
while True:
|
|
180
266
|
if not kernel32.WaitForDebugEvent(ctypes.byref(debug_event), 1000): # 1 sec timeout to yield
|
|
@@ -189,6 +275,11 @@ def debug_process(command, client):
|
|
|
189
275
|
if debug_event.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT:
|
|
190
276
|
process_handle = debug_event.u.CreateProcessInfo.hProcess
|
|
191
277
|
client.send("System", "info", {"text": f"Process Created: PID {debug_event.dwProcessId}"})
|
|
278
|
+
|
|
279
|
+
# Start psutil daemon for disk and network tracking
|
|
280
|
+
daemon_thread = threading.Thread(target=psutil_daemon, args=(debug_event.dwProcessId, client, daemon_stop_event), daemon=True)
|
|
281
|
+
daemon_thread.start()
|
|
282
|
+
|
|
192
283
|
if debug_event.u.CreateProcessInfo.hFile:
|
|
193
284
|
kernel32.CloseHandle(debug_event.u.CreateProcessInfo.hFile)
|
|
194
285
|
|
|
@@ -217,11 +308,16 @@ def debug_process(command, client):
|
|
|
217
308
|
|
|
218
309
|
elif debug_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT:
|
|
219
310
|
client.send("System", "info", {"text": f"Process Exited with code {debug_event.u.ExitProcess.dwExitCode}"})
|
|
311
|
+
daemon_stop_event.set()
|
|
220
312
|
kernel32.ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, continue_status)
|
|
221
313
|
break
|
|
222
314
|
|
|
223
315
|
kernel32.ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, continue_status)
|
|
224
316
|
|
|
317
|
+
daemon_stop_event.set()
|
|
318
|
+
if daemon_thread:
|
|
319
|
+
daemon_thread.join(timeout=1.0)
|
|
320
|
+
|
|
225
321
|
if process_handle:
|
|
226
322
|
kernel32.CloseHandle(pi.hThread)
|
|
227
323
|
kernel32.CloseHandle(pi.hProcess)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: h4-debug
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Advanced application proxy debugger and F12-style web console
|
|
5
5
|
Author-email: h4 <h4@example.com>
|
|
6
6
|
Requires-Python: >=3.8
|
|
@@ -9,6 +9,7 @@ Requires-Dist: fastapi>=0.100
|
|
|
9
9
|
Requires-Dist: uvicorn[standard]>=0.20
|
|
10
10
|
Requires-Dist: websockets>=11.0
|
|
11
11
|
Requires-Dist: Pillow>=10.0.0
|
|
12
|
+
Requires-Dist: psutil>=5.9.0
|
|
12
13
|
|
|
13
14
|
# h4-debug
|
|
14
15
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "h4-debug"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "Advanced application proxy debugger and F12-style web console"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -16,6 +16,7 @@ dependencies = [
|
|
|
16
16
|
"uvicorn[standard]>=0.20",
|
|
17
17
|
"websockets>=11.0",
|
|
18
18
|
"Pillow>=10.0.0",
|
|
19
|
+
"psutil>=5.9.0",
|
|
19
20
|
]
|
|
20
21
|
|
|
21
22
|
[project.scripts]
|
|
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
|
|
File without changes
|