h4-debug 0.2.0__tar.gz → 0.2.2__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.2}/PKG-INFO +2 -1
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug/win_debugger.py +105 -2
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug.egg-info/PKG-INFO +2 -1
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug.egg-info/requires.txt +1 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/pyproject.toml +2 -1
- {h4_debug-0.2.0 → h4_debug-0.2.2}/README.md +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug/__init__.py +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug/cli.py +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug/handlers.py +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug/interceptor.py +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug/node_interceptor.js +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug/server.py +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug/static/dashboard.css +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug/static/dashboard.html +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug/static/dashboard.js +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug.egg-info/SOURCES.txt +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug.egg-info/dependency_links.txt +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug.egg-info/entry_points.txt +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/h4_debug.egg-info/top_level.txt +0 -0
- {h4_debug-0.2.0 → h4_debug-0.2.2}/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.2
|
|
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,117 @@ 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
|
+
err = ctypes.GetLastError()
|
|
232
|
+
err_msg = f"Failed to CreateProcess: {err}"
|
|
233
|
+
if err == 5:
|
|
234
|
+
err_msg += " (Access Denied). The executable may require Administrator privileges, or it may be blocked by Windows Defender/DRM. Try running h4-debug from an Administrator terminal."
|
|
235
|
+
elif err == 740:
|
|
236
|
+
err_msg += " (Elevation Required). The executable requires Administrator privileges. Please run h4-debug from an Administrator terminal."
|
|
237
|
+
client.send("System", "error", {"text": err_msg})
|
|
238
|
+
print(f"[h4-debug] {err_msg}")
|
|
239
|
+
if hWritePipe:
|
|
240
|
+
kernel32.CloseHandle(hReadPipe)
|
|
241
|
+
kernel32.CloseHandle(hWritePipe)
|
|
174
242
|
return
|
|
175
243
|
|
|
244
|
+
# Close our write end of the pipe so the read thread unblocks when the process exits
|
|
245
|
+
if hWritePipe:
|
|
246
|
+
kernel32.CloseHandle(hWritePipe)
|
|
247
|
+
|
|
248
|
+
def stream_pipe(handle, client):
|
|
249
|
+
buffer = ctypes.create_string_buffer(4096)
|
|
250
|
+
bytes_read = wintypes.DWORD()
|
|
251
|
+
while True:
|
|
252
|
+
if not kernel32.ReadFile(handle, buffer, 4096, ctypes.byref(bytes_read), None) or bytes_read.value == 0:
|
|
253
|
+
break
|
|
254
|
+
text = buffer.raw[:bytes_read.value].decode('utf-8', 'replace').strip('\r\n\x00')
|
|
255
|
+
if text:
|
|
256
|
+
for line in text.splitlines():
|
|
257
|
+
client.send("Console", "stdout", {"text": line})
|
|
258
|
+
kernel32.CloseHandle(handle)
|
|
259
|
+
|
|
260
|
+
pipe_thread = None
|
|
261
|
+
if hReadPipe:
|
|
262
|
+
pipe_thread = threading.Thread(target=stream_pipe, args=(hReadPipe, client), daemon=True)
|
|
263
|
+
pipe_thread.start()
|
|
264
|
+
|
|
176
265
|
process_handle = None
|
|
177
266
|
debug_event = DEBUG_EVENT()
|
|
267
|
+
|
|
268
|
+
import threading
|
|
269
|
+
daemon_stop_event = threading.Event()
|
|
270
|
+
daemon_thread = None
|
|
178
271
|
|
|
179
272
|
while True:
|
|
180
273
|
if not kernel32.WaitForDebugEvent(ctypes.byref(debug_event), 1000): # 1 sec timeout to yield
|
|
@@ -189,6 +282,11 @@ def debug_process(command, client):
|
|
|
189
282
|
if debug_event.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT:
|
|
190
283
|
process_handle = debug_event.u.CreateProcessInfo.hProcess
|
|
191
284
|
client.send("System", "info", {"text": f"Process Created: PID {debug_event.dwProcessId}"})
|
|
285
|
+
|
|
286
|
+
# Start psutil daemon for disk and network tracking
|
|
287
|
+
daemon_thread = threading.Thread(target=psutil_daemon, args=(debug_event.dwProcessId, client, daemon_stop_event), daemon=True)
|
|
288
|
+
daemon_thread.start()
|
|
289
|
+
|
|
192
290
|
if debug_event.u.CreateProcessInfo.hFile:
|
|
193
291
|
kernel32.CloseHandle(debug_event.u.CreateProcessInfo.hFile)
|
|
194
292
|
|
|
@@ -217,11 +315,16 @@ def debug_process(command, client):
|
|
|
217
315
|
|
|
218
316
|
elif debug_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT:
|
|
219
317
|
client.send("System", "info", {"text": f"Process Exited with code {debug_event.u.ExitProcess.dwExitCode}"})
|
|
318
|
+
daemon_stop_event.set()
|
|
220
319
|
kernel32.ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, continue_status)
|
|
221
320
|
break
|
|
222
321
|
|
|
223
322
|
kernel32.ContinueDebugEvent(debug_event.dwProcessId, debug_event.dwThreadId, continue_status)
|
|
224
323
|
|
|
324
|
+
daemon_stop_event.set()
|
|
325
|
+
if daemon_thread:
|
|
326
|
+
daemon_thread.join(timeout=1.0)
|
|
327
|
+
|
|
225
328
|
if process_handle:
|
|
226
329
|
kernel32.CloseHandle(pi.hThread)
|
|
227
330
|
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.2
|
|
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.2"
|
|
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
|