BinSentry 1.0.0__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.
@@ -0,0 +1,906 @@
1
+ import json
2
+ import socket
3
+ from urllib.parse import urlparse
4
+ from typing import List, Dict, Union, Optional, Callable
5
+ from functools import wraps
6
+
7
+ def check_server_available(func: Callable) -> Callable:
8
+ """装饰器:检测服务是否可用,不可用直接返回error json"""
9
+ @wraps(func)
10
+ def wrapper(self, *args, **kwargs):
11
+ if not self.config.is_server_available():
12
+ return json.dumps({
13
+ "status": "error",
14
+ "message": "Server unavailable"
15
+ }, ensure_ascii=False)
16
+ return func(self, *args, **kwargs)
17
+ return wrapper
18
+
19
+ def validate_hex_address(address: Union[int, str]) -> Optional[str]:
20
+ """校验并格式化十六进制地址,返回 0xxxxx 格式字符串,非法返回None"""
21
+ if isinstance(address, int):
22
+ return hex(address)
23
+ addr_str = str(address).strip()
24
+ if not addr_str:
25
+ return None
26
+ if addr_str.startswith(('0x', '0X')):
27
+ try:
28
+ int(addr_str, 16)
29
+ return addr_str
30
+ except ValueError:
31
+ return None
32
+ try:
33
+ int(addr_str, 10)
34
+ return addr_str
35
+ except ValueError:
36
+ return None
37
+
38
+ class Config:
39
+ def __init__(self, address: str = "127.0.0.1", port: int = 6891):
40
+ self.address = address
41
+ self.port = port
42
+ self.server_addr = f"http://{address}:{port}"
43
+ self.timeout = 5
44
+
45
+ def is_server_available(self, timeout: Optional[int] = None) -> bool:
46
+ """检测端口是否开放"""
47
+ timeout = timeout or self.timeout
48
+ try:
49
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
50
+ sock.settimeout(timeout)
51
+ result = sock.connect_ex((self.address, self.port))
52
+ return result == 0
53
+ except socket.error as e:
54
+ print(f"WARNING: Server check failed: {str(e)}")
55
+ return False
56
+
57
+ class BaseHttpClient:
58
+ def __init__(self, config: Optional[Config] = None):
59
+ self.config = config or Config()
60
+ parsed_url = urlparse(self.config.server_addr)
61
+ self.address = parsed_url.hostname
62
+ self.port = parsed_url.port
63
+ self.scheme = parsed_url.scheme
64
+ self.path = parsed_url.path or '/'
65
+ self.verify_ssl = True
66
+
67
+ def custom_post(self, payload: Optional[Dict] = None, timeout: Optional[int] = None) -> str:
68
+ """发送POST请求,payload对应BinSentry接口 {interface, params}"""
69
+ import http.client
70
+ headers = {"Content-Type": "application/json"}
71
+ body = json.dumps(payload).encode("utf-8") if payload else None
72
+ timeout = timeout or self.config.timeout
73
+ try:
74
+ if self.scheme == "https":
75
+ import ssl
76
+ context = ssl._create_unverified_context() if not self.verify_ssl else None
77
+ conn = http.client.HTTPSConnection(self.address, self.port, timeout=timeout, context=context)
78
+ else:
79
+ conn = http.client.HTTPConnection(self.address, self.port, timeout=timeout)
80
+ conn.request("POST", self.path, body=body, headers=headers)
81
+ response = conn.getresponse()
82
+ response_text = response.read().decode("utf-8", errors="ignore")
83
+ conn.close()
84
+ return response_text
85
+ except socket.timeout:
86
+ return json.dumps({"status": "error", "message": "Request timed out"}, ensure_ascii=False)
87
+ except ConnectionRefusedError:
88
+ return json.dumps({"status": "error", "message": "Connection refused by server"}, ensure_ascii=False)
89
+ except Exception as e:
90
+ return json.dumps({"status": "error", "message": f"Request failed: {str(e)}"}, ensure_ascii=False)
91
+
92
+ class SystemApi(BaseHttpClient):
93
+ """系统与进程相关接口"""
94
+ @check_server_available
95
+ def system_info(self) -> str:
96
+ return self.custom_post({"interface": "SystemInfo", "params": {}})
97
+
98
+ @check_server_available
99
+ def process_list(self) -> str:
100
+ return self.custom_post({"interface": "ProcessList", "params": {}})
101
+
102
+ @check_server_available
103
+ def enum_windows(self, pid: int) -> str:
104
+ return self.custom_post({"interface": "EnumWindows", "params": {"pid": pid}})
105
+
106
+ @check_server_available
107
+ def is_process_elevated(self) -> str:
108
+ return self.custom_post({"interface": "IsProcessElevated", "params": {}})
109
+
110
+ @check_server_available
111
+ def enum_error_codes(self) -> str:
112
+ return self.custom_post({"interface": "EnumErrorCodes", "params": {}})
113
+
114
+ @check_server_available
115
+ def enum_exceptions(self) -> str:
116
+ return self.custom_post({"interface": "EnumExceptions", "params": {}})
117
+
118
+ @check_server_available
119
+ def help(self) -> str:
120
+ return self.custom_post({"interface": "Help", "params": {}})
121
+
122
+ @check_server_available
123
+ def get_jit(self) -> str:
124
+ return self.custom_post({"interface": "GetJIT", "params": {}})
125
+
126
+ @check_server_available
127
+ def set_jit(self, path: str) -> str:
128
+ return self.custom_post({"interface": "SetJIT", "params": {"path": path}})
129
+
130
+ @check_server_available
131
+ def get_command_line(self) -> str:
132
+ return self.custom_post({"interface": "GetCommandLine", "params": {}})
133
+
134
+ @check_server_available
135
+ def set_command_line(self, args: str) -> str:
136
+ return self.custom_post({"interface": "SetCommandLine", "params": {"args": args}})
137
+
138
+ class LogConfigApi(BaseHttpClient):
139
+ """日志与配置数据库"""
140
+ @check_server_available
141
+ def clear_log(self) -> str:
142
+ return self.custom_post({"interface": "ClearLog", "params": {}})
143
+
144
+ @check_server_available
145
+ def logs(self) -> str:
146
+ return self.custom_post({"interface": "Logs", "params": {}})
147
+
148
+ @check_server_available
149
+ def save_log(self, path: str) -> str:
150
+ return self.custom_post({"interface": "SaveLog", "params": {"path": path}})
151
+
152
+ @check_server_available
153
+ def load_database(self, path: str) -> str:
154
+ return self.custom_post({"interface": "LoadDatabase", "params": {"path": path}})
155
+
156
+ @check_server_available
157
+ def save_database(self, path: str) -> str:
158
+ return self.custom_post({"interface": "SaveDatabase", "params": {"path": path}})
159
+
160
+ @check_server_available
161
+ def load_config(self, module: str) -> str:
162
+ return self.custom_post({"interface": "LoadConfig", "params": {"module": module}})
163
+
164
+ class SymbolVarApi(BaseHttpClient):
165
+ """变量、参数、注释、标签、函数"""
166
+ @check_server_available
167
+ def set_var(self, name: str, value: str) -> str:
168
+ return self.custom_post({"interface": "SetVar", "params": {"name": name, "value": value}})
169
+
170
+ @check_server_available
171
+ def del_var(self, name: str) -> str:
172
+ return self.custom_post({"interface": "DelVar", "params": {"name": name}})
173
+
174
+ @check_server_available
175
+ def get_vars(self) -> str:
176
+ return self.custom_post({"interface": "GetVars", "params": {}})
177
+
178
+ @check_server_available
179
+ def add_argument(self, start: Union[int, str], end: Union[int, str] = "", name: str = "") -> str:
180
+ start_addr = validate_hex_address(start)
181
+ payload: Dict = {"start": start_addr}
182
+ if end:
183
+ payload["end"] = validate_hex_address(end)
184
+ if name:
185
+ payload["name"] = name
186
+ return self.custom_post({"interface": "AddArgument", "params": payload})
187
+
188
+ @check_server_available
189
+ def del_argument(self, start: Union[int, str]) -> str:
190
+ s = validate_hex_address(start)
191
+ return self.custom_post({"interface": "DelArgument", "params": {"start": s}})
192
+
193
+ @check_server_available
194
+ def get_arguments(self) -> str:
195
+ return self.custom_post({"interface": "GetArguments", "params": {}})
196
+
197
+ @check_server_available
198
+ def get_comments(self) -> str:
199
+ return self.custom_post({"interface": "GetComments", "params": {}})
200
+
201
+ @check_server_available
202
+ def get_labels(self) -> str:
203
+ return self.custom_post({"interface": "GetLabels", "params": {}})
204
+
205
+ @check_server_available
206
+ def get_bookmarks(self) -> str:
207
+ return self.custom_post({"interface": "GetBookMarks", "params": {}})
208
+
209
+ @check_server_available
210
+ def functions(self) -> str:
211
+ return self.custom_post({"interface": "Functions", "params": {}})
212
+
213
+ class BreakPointApi(BaseHttpClient):
214
+ """断点:软件/硬件/内存/api/异常/dll断点"""
215
+ @check_server_available
216
+ def show_breakpoint(self) -> str:
217
+ return self.custom_post({"interface": "ShowBreakPoint", "params": {}})
218
+
219
+ @check_server_available
220
+ def show_hbreakpoint(self) -> str:
221
+ return self.custom_post({"interface": "ShowHbreakPoint", "params": {}})
222
+
223
+ @check_server_available
224
+ def show_mem_breakpoint(self) -> str:
225
+ return self.custom_post({"interface": "ShowMemBreakPoint", "params": {}})
226
+
227
+ @check_server_available
228
+ def show_api_breakpoint(self) -> str:
229
+ return self.custom_post({"interface": "ShowApiBreakPoint", "params": {}})
230
+
231
+ @check_server_available
232
+ def set_bpx_options(self, option: str, enable: int) -> str:
233
+ return self.custom_post({"interface": "SetBPXOptions", "params": {"option": option, "enable": enable}})
234
+
235
+ @check_server_available
236
+ def set_exception_bpx(self, code: str) -> str:
237
+ return self.custom_post({"interface": "SetExceptionBPX", "params": {"code": code}})
238
+
239
+ @check_server_available
240
+ def del_exception_bpx(self, code: str) -> str:
241
+ return self.custom_post({"interface": "DelExceptionBPX", "params": {"code": code}})
242
+
243
+ @check_server_available
244
+ def get_exception_bpx_list(self) -> str:
245
+ return self.custom_post({"interface": "GetExceptionBPXList", "params": {}})
246
+
247
+ @check_server_available
248
+ def set_dll_breakpoint(self, dll: str) -> str:
249
+ return self.custom_post({"interface": "SetDllBreakPoint", "params": {"dll": dll}})
250
+
251
+ @check_server_available
252
+ def del_dll_breakpoint(self, dll: str) -> str:
253
+ return self.custom_post({"interface": "DelDllBreakPoint", "params": {"dll": dll}})
254
+
255
+ @check_server_available
256
+ def get_dll_breakpoints(self) -> str:
257
+ return self.custom_post({"interface": "GetDllBreakPoints", "params": {}})
258
+
259
+ @check_server_available
260
+ def set_breakpoint(self, address: Union[int, str]) -> str:
261
+ addr = validate_hex_address(address)
262
+ return self.custom_post({"interface": "SetBreakPoint", "params": {"address": addr}})
263
+
264
+ @check_server_available
265
+ def del_breakpoint(self, address: Union[int, str]) -> str:
266
+ addr = validate_hex_address(address)
267
+ return self.custom_post({"interface": "DelBreakPoint", "params": {"address": addr}})
268
+
269
+ @check_server_available
270
+ def get_breakpoint_info(self, address: Union[int, str]) -> str:
271
+ addr = validate_hex_address(address)
272
+ return self.custom_post({"interface": "GetBreakpointInfo", "params": {"address": addr}})
273
+
274
+ @check_server_available
275
+ def get_breakpoint_type(self, address: Union[int, str]) -> str:
276
+ addr = validate_hex_address(address)
277
+ return self.custom_post({"interface": "GetBreakpointType", "params": {"address": addr}})
278
+
279
+ @check_server_available
280
+ def set_breakpoint_name(self, address: Union[int, str], name: str) -> str:
281
+ addr = validate_hex_address(address)
282
+ return self.custom_post({"interface": "SetBreakpointName", "params": {"address": addr, "name": name}})
283
+
284
+ @check_server_available
285
+ def set_breakpoint_singleshoot(self, address: Union[int, str], enable: int = 0) -> str:
286
+ addr = validate_hex_address(address)
287
+ return self.custom_post({"interface": "SetBreakpointSingleshoot", "params": {"address": addr, "enable": enable}})
288
+
289
+ @check_server_available
290
+ def set_breakpoint_fast_resume(self, address: Union[int, str], enable: int = 0) -> str:
291
+ addr = validate_hex_address(address)
292
+ return self.custom_post({"interface": "SetBreakpointFastResume", "params": {"address": addr, "enable": enable}})
293
+
294
+ @check_server_available
295
+ def set_breakpoint_silent(self, address: Union[int, str], enable: int = 0) -> str:
296
+ addr = validate_hex_address(address)
297
+ return self.custom_post({"interface": "SetBreakpointSilent", "params": {"address": addr, "enable": enable}})
298
+
299
+ @check_server_available
300
+ def set_breakpoint_log(self, address: Union[int, str], text: str) -> str:
301
+ addr = validate_hex_address(address)
302
+ return self.custom_post({"interface": "SetBreakpointLog", "params": {"address": addr, "text": text}})
303
+
304
+ @check_server_available
305
+ def set_breakpoint_log_file(self, address: Union[int, str], file: str) -> str:
306
+ addr = validate_hex_address(address)
307
+ return self.custom_post({"interface": "SetBreakpointLogFile", "params": {"address": addr, "file": file}})
308
+
309
+ @check_server_available
310
+ def set_breakpoint_hit_count(self, address: Union[int, str], count: int) -> str:
311
+ addr = validate_hex_address(address)
312
+ return self.custom_post({"interface": "SetBreakpointHitCount", "params": {"address": addr, "count": count}})
313
+
314
+ @check_server_available
315
+ def get_breakpoint_hit_count(self, address: Union[int, str]) -> str:
316
+ addr = validate_hex_address(address)
317
+ return self.custom_post({"interface": "GetBreakpointHitCount", "params": {"address": addr}})
318
+
319
+ @check_server_available
320
+ def reset_breakpoint_hit_count(self, address: Union[int, str]) -> str:
321
+ addr = validate_hex_address(address)
322
+ return self.custom_post({"interface": "ResetBreakpointHitCount", "params": {"address": addr}})
323
+
324
+ @check_server_available
325
+ def set_cond_breakpoint(self, address: Union[int, str], cond: str, thread: int = 0) -> str:
326
+ addr = validate_hex_address(address)
327
+ return self.custom_post({"interface": "SetCondBreakPoint", "params": {"address": addr, "cond": cond, "thread": thread}})
328
+
329
+ @check_server_available
330
+ def del_cond_breakpoint(self, address: Union[int, str]) -> str:
331
+ addr = validate_hex_address(address)
332
+ return self.custom_post({"interface": "DelCondBreakPoint", "params": {"address": addr}})
333
+
334
+ @check_server_available
335
+ def set_hbreakpoint(self, address: Union[int, str], len_: str = "1", flag: str = "e") -> str:
336
+ addr = validate_hex_address(address)
337
+ return self.custom_post({"interface": "SetHbreakPoint", "params": {"address": addr, "len": len_, "flag": flag}})
338
+
339
+ @check_server_available
340
+ def del_hbreakpoint(self, address: Union[int, str]) -> str:
341
+ addr = validate_hex_address(address)
342
+ return self.custom_post({"interface": "DelHbreakPoint", "params": {"address": addr}})
343
+
344
+ @check_server_available
345
+ def set_mem_breakpoint(self, address: Union[int, str], flag: str = "e") -> str:
346
+ addr = validate_hex_address(address)
347
+ return self.custom_post({"interface": "SetMemBreakPoint", "params": {"address": addr, "flag": flag}})
348
+
349
+ @check_server_available
350
+ def del_mem_breakpoint(self, address: Union[int, str]) -> str:
351
+ addr = validate_hex_address(address)
352
+ return self.custom_post({"interface": "DelMemBreakPoint", "params": {"address": addr}})
353
+
354
+ @check_server_available
355
+ def set_api_breakpoint(self, dll: str, api: str) -> str:
356
+ return self.custom_post({"interface": "SetApiBreakPoint", "params": {"dll": dll, "api": api}})
357
+
358
+ @check_server_available
359
+ def del_api_breakpoint(self, dll: str, api: str) -> str:
360
+ return self.custom_post({"interface": "DelApiBreakPoint", "params": {"dll": dll, "api": api}})
361
+
362
+ @check_server_available
363
+ def disable_breakpoint(self, address: Union[int, str]) -> str:
364
+ addr = validate_hex_address(address)
365
+ return self.custom_post({"interface": "DisableBreakpoint", "params": {"address": addr}})
366
+
367
+ @check_server_available
368
+ def enable_breakpoint(self, address: Union[int, str]) -> str:
369
+ addr = validate_hex_address(address)
370
+ return self.custom_post({"interface": "EnableBreakpoint", "params": {"address": addr}})
371
+
372
+ @check_server_available
373
+ def breakpoint_command(self, address: Union[int, str], command: str = "") -> str:
374
+ addr = validate_hex_address(address)
375
+ return self.custom_post({"interface": "BreakpointCommand", "params": {"address": addr, "command": command}})
376
+
377
+ class DebugSessionApi(BaseHttpClient):
378
+ """会话管理:启动调试、停止、分离、dump"""
379
+ @check_server_available
380
+ def debug(self, path: str, args: str = "", cwd: str = "") -> str:
381
+ return self.custom_post({"interface": "Debug", "params": {"path": path, "args": args, "cwd": cwd}})
382
+
383
+ @check_server_available
384
+ def set_debug(self, command: str = "Logs") -> str:
385
+ return self.custom_post({"interface": "SetDebug", "params": command})
386
+
387
+ @check_server_available
388
+ def stop(self) -> str:
389
+ return self.custom_post({"interface": "Stop", "params": {}})
390
+
391
+ @check_server_available
392
+ def detach(self) -> str:
393
+ return self.custom_post({"interface": "Detach", "params": {}})
394
+
395
+ @check_server_available
396
+ def restart(self) -> str:
397
+ return self.custom_post({"interface": "Restart", "params": {}})
398
+
399
+ @check_server_available
400
+ def dump_process(self, path: str, base: Union[int, str], size: int) -> str:
401
+ base_addr = validate_hex_address(base)
402
+ return self.custom_post({"interface": "DumpProcess", "params": {"path": path, "base": base_addr, "size": size}})
403
+
404
+ @check_server_available
405
+ def minidump(self, path: str) -> str:
406
+ return self.custom_post({"interface": "minidump", "params": {"path": path}})
407
+
408
+ @check_server_available
409
+ def status(self) -> str:
410
+ return self.custom_post({"interface": "Status", "params": {}})
411
+
412
+ class RegisterThreadApi(BaseHttpClient):
413
+ """寄存器、线程操作"""
414
+ @check_server_available
415
+ def register(self) -> str:
416
+ return self.custom_post({"interface": "Register", "params": {}})
417
+
418
+ @check_server_available
419
+ def set_register(self, reg: str, value: str) -> str:
420
+ return self.custom_post({"interface": "SetRegister", "params": {"reg": reg, "value": value}})
421
+
422
+ @check_server_available
423
+ def threads(self) -> str:
424
+ return self.custom_post({"interface": "Threads", "params": {}})
425
+
426
+ @check_server_available
427
+ def thread_info(self, tid: int) -> str:
428
+ return self.custom_post({"interface": "ThreadInfo", "params": {"tid": tid}})
429
+
430
+ @check_server_available
431
+ def get_active_thread(self) -> str:
432
+ return self.custom_post({"interface": "GetActiveThread", "params": {}})
433
+
434
+ @check_server_available
435
+ def set_active_thread(self, tid: int) -> str:
436
+ return self.custom_post({"interface": "SetActiveThread", "params": {"tid": tid}})
437
+
438
+ @check_server_available
439
+ def get_thread_last_error(self, tid: int) -> str:
440
+ return self.custom_post({"interface": "GetThreadLastError", "params": {"tid": tid}})
441
+
442
+ @check_server_available
443
+ def get_thread_priority(self, tid: int) -> str:
444
+ return self.custom_post({"interface": "GetThreadPriority", "params": {"tid": tid}})
445
+
446
+ @check_server_available
447
+ def set_thread_priority(self, tid: int, priority: str = "NORMAL") -> str:
448
+ return self.custom_post({"interface": "SetThreadPriority", "params": {"tid": tid, "priority": priority}})
449
+
450
+ @check_server_available
451
+ def set_thread_name(self, tid: int, name: str) -> str:
452
+ return self.custom_post({"interface": "SetThreadName", "params": {"tid": tid, "name": name}})
453
+
454
+ @check_server_available
455
+ def set_flag(self, flag: str, value: int) -> str:
456
+ return self.custom_post({"interface": "SetFlag", "params": {"flag": flag, "value": value}})
457
+
458
+ class ModulePeApi(BaseHttpClient):
459
+ """模块、PE、导入导出、符号"""
460
+ @check_server_available
461
+ def modules(self) -> str:
462
+ return self.custom_post({"interface": "Modules", "params": {}})
463
+
464
+ @check_server_available
465
+ def module_info(self, module: str) -> str:
466
+ return self.custom_post({"interface": "ModuleInfo", "params": {"module": module}})
467
+
468
+ @check_server_available
469
+ def sections(self, module: str) -> str:
470
+ return self.custom_post({"interface": "Sections", "params": {"module": module}})
471
+
472
+ @check_server_available
473
+ def pe_info(self, path: str) -> str:
474
+ return self.custom_post({"interface": "PEInfo", "params": {"path": path}})
475
+
476
+ @check_server_available
477
+ def rich_header(self, module: str) -> str:
478
+ return self.custom_post({"interface": "RichHeader", "params": {"module": module}})
479
+
480
+ @check_server_available
481
+ def tls_callbacks(self, module: str) -> str:
482
+ return self.custom_post({"interface": "TLSCallbacks", "params": {"module": module}})
483
+
484
+ @check_server_available
485
+ def relocation_list(self, module: str) -> str:
486
+ return self.custom_post({"interface": "RelocationList", "params": {"module": module}})
487
+
488
+ @check_server_available
489
+ def debug_directory(self, module: str) -> str:
490
+ return self.custom_post({"interface": "DebugDirectory", "params": {"module": module}})
491
+
492
+ @check_server_available
493
+ def import_list(self, module: str) -> str:
494
+ return self.custom_post({"interface": "ImportList", "params": {"module": module}})
495
+
496
+ @check_server_available
497
+ def export_list(self, module: str) -> str:
498
+ return self.custom_post({"interface": "ExportList", "params": {"module": module}})
499
+
500
+ @check_server_available
501
+ def get_import_address(self, module: str, name: str) -> str:
502
+ return self.custom_post({"interface": "GetImportAddress", "params": {"module": module, "name": name}})
503
+
504
+ @check_server_available
505
+ def get_export_address(self, module: str, name: str) -> str:
506
+ return self.custom_post({"interface": "GetExportAddress", "params": {"module": module, "name": name}})
507
+
508
+ @check_server_available
509
+ def gpa(self, dll: str, api: str) -> str:
510
+ return self.custom_post({"interface": "gpa", "params": {"dll": dll, "api": api}})
511
+
512
+ @check_server_available
513
+ def addr_to_module(self, address: Union[int, str]) -> str:
514
+ addr = validate_hex_address(address)
515
+ return self.custom_post({"interface": "AddrToModule", "params": {"address": addr}})
516
+
517
+ @check_server_available
518
+ def get_section_data(self, module: str, name: str) -> str:
519
+ return self.custom_post({"interface": "GetSectionData", "params": {"module": module, "name": name}})
520
+
521
+ @check_server_available
522
+ def get_section_info(self, module: str, name: str) -> str:
523
+ return self.custom_post({"interface": "GetSectionInfo", "params": {"module": module, "name": name}})
524
+
525
+ @check_server_available
526
+ def symbol(self, expr: str) -> str:
527
+ return self.custom_post({"interface": "Symbol", "params": {"expr": expr}})
528
+
529
+ @check_server_available
530
+ def get_symbol_info(self, address: Union[int, str]) -> str:
531
+ addr = validate_hex_address(address)
532
+ return self.custom_post({"interface": "GetSymbolInfo", "params": {"address": addr}})
533
+
534
+ @check_server_available
535
+ def get_function_size(self, address: Union[int, str]) -> str:
536
+ addr = validate_hex_address(address)
537
+ return self.custom_post({"interface": "GetFunctionSize", "params": {"address": addr}})
538
+
539
+ @check_server_available
540
+ def heaps(self) -> str:
541
+ return self.custom_post({"interface": "Heaps", "params": {}})
542
+
543
+ @check_server_available
544
+ def handles(self, max_num: int = 20) -> str:
545
+ return self.custom_post({"interface": "Handles", "params": {"max": max_num}})
546
+
547
+ @check_server_available
548
+ def seh_list(self) -> str:
549
+ return self.custom_post({"interface": "SEHList", "params": {}})
550
+
551
+ class MemoryApi(BaseHttpClient):
552
+ """内存读写、搜索、补丁、内存分配"""
553
+ @check_server_available
554
+ def memory_info(self, address: Union[int, str]) -> str:
555
+ addr = validate_hex_address(address)
556
+ return self.custom_post({"interface": "MemoryInfo", "params": {"address": addr}})
557
+
558
+ @check_server_available
559
+ def regions(self) -> str:
560
+ return self.custom_post({"interface": "Regions", "params": {}})
561
+
562
+ @check_server_available
563
+ def memory(self, address: Union[int, str], size: int = 16) -> str:
564
+ addr = validate_hex_address(address)
565
+ return self.custom_post({"interface": "Memory", "params": {"address": addr, "size": size}})
566
+
567
+ @check_server_available
568
+ def read_memory_value(self, address: Union[int, str], size: int = 4) -> str:
569
+ addr = validate_hex_address(address)
570
+ return self.custom_post({"interface": "ReadMemoryValue", "params": {"address": addr, "size": size}})
571
+
572
+ @check_server_available
573
+ def write_memory(self, address: Union[int, str], hex_: str) -> str:
574
+ addr = validate_hex_address(address)
575
+ return self.custom_post({"interface": "WriteMemory", "params": {"address": addr, "hex": hex_}})
576
+
577
+ @check_server_available
578
+ def get_page_rights(self, address: Union[int, str]) -> str:
579
+ addr = validate_hex_address(address)
580
+ return self.custom_post({"interface": "GetPageRights", "params": {"address": addr}})
581
+
582
+ @check_server_available
583
+ def set_page_rights(self, address: Union[int, str], protect: str) -> str:
584
+ addr = validate_hex_address(address)
585
+ return self.custom_post({"interface": "SetPageRights", "params": {"address": addr, "protect": protect}})
586
+
587
+ @check_server_available
588
+ def set_page_memory(self, address: Union[int, str], protect: str) -> str:
589
+ addr = validate_hex_address(address)
590
+ return self.custom_post({"interface": "SetPageMemory", "params": {"address": addr, "protect": protect}})
591
+
592
+ @check_server_available
593
+ def allocate_memory(self, size: int) -> str:
594
+ return self.custom_post({"interface": "AllocateMemory", "params": {"size": size}})
595
+
596
+ @check_server_available
597
+ def free_memory(self, address: Union[int, str]) -> str:
598
+ addr = validate_hex_address(address)
599
+ return self.custom_post({"interface": "FreeMemory", "params": {"address": addr}})
600
+
601
+ @check_server_available
602
+ def set_memory(self, address: Union[int, str], hex_: str) -> str:
603
+ addr = validate_hex_address(address)
604
+ return self.custom_post({"interface": "SetMemory", "params": {"address": addr, "hex": hex_}})
605
+
606
+ @check_server_available
607
+ def fill_memory(self, address: Union[int, str], size: int, value: str) -> str:
608
+ addr = validate_hex_address(address)
609
+ return self.custom_post({"interface": "FillMemory", "params": {"address": addr, "size": size, "value": value}})
610
+
611
+ @check_server_available
612
+ def memcpy(self, src: Union[int, str], dst: Union[int, str], size: int) -> str:
613
+ src_a = validate_hex_address(src)
614
+ dst_a = validate_hex_address(dst)
615
+ return self.custom_post({"interface": "Memcpy", "params": {"src": src_a, "dst": dst_a, "size": size}})
616
+
617
+ @check_server_available
618
+ def va_to_file_offset(self, address: Union[int, str]) -> str:
619
+ addr = validate_hex_address(address)
620
+ return self.custom_post({"interface": "VaToFileOffset", "params": {"address": addr}})
621
+
622
+ @check_server_available
623
+ def file_offset_to_va(self, offset: str) -> str:
624
+ return self.custom_post({"interface": "FileOffsetToVa", "params": {"offset": offset}})
625
+
626
+ @check_server_available
627
+ def get_string(self, address: Union[int, str], max_num: int = 64, type_: str = "ascii") -> str:
628
+ addr = validate_hex_address(address)
629
+ return self.custom_post({"interface": "GetString", "params": {"address": addr, "max": max_num, "type": type_}})
630
+
631
+ @check_server_available
632
+ def search_memory(self, pattern: str, start: Union[int, str], size: int) -> str:
633
+ s = validate_hex_address(start)
634
+ return self.custom_post({"interface": "SearchMemory", "params": {"pattern": pattern, "start": s, "size": size}})
635
+
636
+ @check_server_available
637
+ def search_all_memory(self, pattern: str, max_num: int = 5) -> str:
638
+ return self.custom_post({"interface": "SearchAllMemory", "params": {"pattern": pattern, "max": max_num}})
639
+
640
+ @check_server_available
641
+ def search_strings(self, address: Union[int, str], size: int) -> str:
642
+ addr = validate_hex_address(address)
643
+ return self.custom_post({"interface": "SearchStrings", "params": {"address": addr, "size": size}})
644
+
645
+ @check_server_available
646
+ def match_pattern(self, address: Union[int, str], pattern: str, size: int) -> str:
647
+ addr = validate_hex_address(address)
648
+ return self.custom_post({"interface": "MatchPattern", "params": {"address": addr, "pattern": pattern, "size": size}})
649
+
650
+ @check_server_available
651
+ def hash_memory(self, address: Union[int, str], size: int, algo: str = "md5") -> str:
652
+ addr = validate_hex_address(address)
653
+ return self.custom_post({"interface": "HashMemory", "params": {"address": addr, "size": size, "algo": algo}})
654
+
655
+ @check_server_available
656
+ def patch_memory(self, address: Union[int, str], hex_: str) -> str:
657
+ addr = validate_hex_address(address)
658
+ return self.custom_post({"interface": "PatchMemory", "params": {"address": addr, "hex": hex_}})
659
+
660
+ @check_server_available
661
+ def patches(self) -> str:
662
+ return self.custom_post({"interface": "Patches", "params": {}})
663
+
664
+ @check_server_available
665
+ def revert_patch(self, address: Union[int, str]) -> str:
666
+ addr = validate_hex_address(address)
667
+ return self.custom_post({"interface": "RevertPatch", "params": {"address": addr}})
668
+
669
+ @check_server_available
670
+ def delete_patch(self, address: Union[int, str]) -> str:
671
+ addr = validate_hex_address(address)
672
+ return self.custom_post({"interface": "DeletePatch", "params": {"address": addr}})
673
+
674
+ class DisasmXrefApi(BaseHttpClient):
675
+ """反汇编、汇编、交叉引用"""
676
+ @check_server_available
677
+ def disassemble_at(self, address: Union[int, str], count: int = 5) -> str:
678
+ addr = validate_hex_address(address)
679
+ return self.custom_post({"interface": "DisassembleAt", "params": {"address": addr, "count": count}})
680
+
681
+ @check_server_available
682
+ def dissasembler(self, address: Union[int, str], count: int = 5) -> str:
683
+ addr = validate_hex_address(address)
684
+ return self.custom_post({"interface": "Dissasembler", "params": {"address": addr, "count": count}})
685
+
686
+ @check_server_available
687
+ def get_opcode_size(self, address: Union[int, str]) -> str:
688
+ addr = validate_hex_address(address)
689
+ return self.custom_post({"interface": "GetOpcodeSize", "params": {"address": addr}})
690
+
691
+ @check_server_available
692
+ def mnemonicbrief(self, address: Union[int, str]) -> str:
693
+ addr = validate_hex_address(address)
694
+ return self.custom_post({"interface": "mnemonicbrief", "params": {"address": addr}})
695
+
696
+ @check_server_available
697
+ def get_branch_target(self, address: Union[int, str]) -> str:
698
+ addr = validate_hex_address(address)
699
+ return self.custom_post({"interface": "GetBranchTarget", "params": {"address": addr}})
700
+
701
+ @check_server_available
702
+ def assemble(self, instr: str, cip: Union[int, str]) -> str:
703
+ c = validate_hex_address(cip)
704
+ return self.custom_post({"interface": "Assemble", "params": {"instr": instr, "cip": c}})
705
+
706
+ @check_server_available
707
+ def assemble_at(self, address: Union[int, str], instr: str) -> str:
708
+ addr = validate_hex_address(address)
709
+ return self.custom_post({"interface": "AssembleAt", "params": {"address": addr, "instr": instr}})
710
+
711
+ @check_server_available
712
+ def xrefs(self, address: Union[int, str], max_num: int = 5) -> str:
713
+ addr = validate_hex_address(address)
714
+ return self.custom_post({"interface": "xrefs", "params": {"address": addr, "max": max_num}})
715
+
716
+ @check_server_available
717
+ def find_ref(self, address: Union[int, str], max_num: int = 5) -> str:
718
+ addr = validate_hex_address(address)
719
+ return self.custom_post({"interface": "FindRef", "params": {"address": addr, "max": max_num}})
720
+
721
+ class StackTraceApi(BaseHttpClient):
722
+ """栈、调用栈、追踪记录"""
723
+ @check_server_available
724
+ def call_stack(self, max_num: int = 16) -> str:
725
+ return self.custom_post({"interface": "CallStack", "params": {"max": max_num}})
726
+
727
+ @check_server_available
728
+ def stack(self, count: int = 8) -> str:
729
+ return self.custom_post({"interface": "Stack", "params": {"count": count}})
730
+
731
+ @check_server_available
732
+ def stack_push(self, value: str) -> str:
733
+ return self.custom_post({"interface": "StackPush", "params": {"value": value}})
734
+
735
+ @check_server_available
736
+ def stack_pop(self) -> str:
737
+ return self.custom_post({"interface": "StackPop", "params": {}})
738
+
739
+ @check_server_available
740
+ def stack_peek(self, offset: int = 0) -> str:
741
+ return self.custom_post({"interface": "StackPeek", "params": {"offset": offset}})
742
+
743
+ @check_server_available
744
+ def start_trace_record(self) -> str:
745
+ return self.custom_post({"interface": "StartTraceRecord", "params": {}})
746
+
747
+ @check_server_available
748
+ def get_trace_record(self, count: int = 5) -> str:
749
+ return self.custom_post({"interface": "GetTraceRecord", "params": {"count": count}})
750
+
751
+ @check_server_available
752
+ def stop_trace_record(self) -> str:
753
+ return self.custom_post({"interface": "StopTraceRecord", "params": {}})
754
+
755
+ @check_server_available
756
+ def jmp_history(self) -> str:
757
+ return self.custom_post({"interface": "JmpHistory", "params": {}})
758
+
759
+ @check_server_available
760
+ def last_exception(self) -> str:
761
+ return self.custom_post({"interface": "LastException", "params": {}})
762
+
763
+ @check_server_available
764
+ def eval_expr(self, expr: str) -> str:
765
+ return self.custom_post({"interface": "Eval", "params": {"expr": expr}})
766
+
767
+ @check_server_available
768
+ def show_debugger(self) -> str:
769
+ return self.custom_post({"interface": "ShowDebugger", "params": {}})
770
+
771
+ @check_server_available
772
+ def hide_debugger(self) -> str:
773
+ return self.custom_post({"interface": "HideDebugger", "params": {}})
774
+
775
+ class ExecutionControlApi(BaseHttpClient):
776
+ """执行控制:运行、单步、暂停、run_to"""
777
+ @check_server_available
778
+ def run(self) -> str:
779
+ return self.custom_post({"interface": "Run", "params": {}})
780
+
781
+ @check_server_available
782
+ def pause(self) -> str:
783
+ return self.custom_post({"interface": "Pause", "params": {}})
784
+
785
+ @check_server_available
786
+ def e_run(self) -> str:
787
+ return self.custom_post({"interface": "ERun", "params": {}})
788
+
789
+ @check_server_available
790
+ def se_run(self) -> str:
791
+ return self.custom_post({"interface": "SERun", "params": {}})
792
+
793
+ @check_server_available
794
+ def step_in(self) -> str:
795
+ return self.custom_post({"interface": "StepIn", "params": {}})
796
+
797
+ @check_server_available
798
+ def step_over(self) -> str:
799
+ return self.custom_post({"interface": "StepOver", "params": {}})
800
+
801
+ @check_server_available
802
+ def step_out(self) -> str:
803
+ return self.custom_post({"interface": "StepOut", "params": {}})
804
+
805
+ @check_server_available
806
+ def e_step_into(self) -> str:
807
+ return self.custom_post({"interface": "EStepInto", "params": {}})
808
+
809
+ @check_server_available
810
+ def e_step_over(self) -> str:
811
+ return self.custom_post({"interface": "EStepOver", "params": {}})
812
+
813
+ @check_server_available
814
+ def e_step_out(self) -> str:
815
+ return self.custom_post({"interface": "EStepOut", "params": {}})
816
+
817
+ @check_server_available
818
+ def step_user(self) -> str:
819
+ return self.custom_post({"interface": "StepUser", "params": {}})
820
+
821
+ @check_server_available
822
+ def step_system(self) -> str:
823
+ return self.custom_post({"interface": "StepSystem", "params": {}})
824
+
825
+ @check_server_available
826
+ def skip(self, count: int = 1) -> str:
827
+ return self.custom_post({"interface": "Skip", "params": {"count": count}})
828
+
829
+ @check_server_available
830
+ def instr_undo(self) -> str:
831
+ return self.custom_post({"interface": "InstrUndo", "params": {}})
832
+
833
+ @check_server_available
834
+ def execute_command(self, command: str) -> str:
835
+ return self.custom_post({"interface": "ExecuteCommand", "params": command})
836
+
837
+ @check_server_available
838
+ def trace_into(self, count: int = 3) -> str:
839
+ return self.custom_post({"interface": "TraceInto", "params": {"count": count}})
840
+
841
+ @check_server_available
842
+ def trace_over(self, count: int = 3) -> str:
843
+ return self.custom_post({"interface": "TraceOver", "params": {"count": count}})
844
+
845
+ @check_server_available
846
+ def trace_line(self, address: Union[int, str]) -> str:
847
+ addr = validate_hex_address(address)
848
+ return self.custom_post({"interface": "TraceLine", "params": {"address": addr}})
849
+
850
+ @check_server_available
851
+ def run_to(self, address: Union[int, str]) -> str:
852
+ addr = validate_hex_address(address)
853
+ return self.custom_post({"interface": "RunTo", "params": {"address": addr}})
854
+
855
+ @check_server_available
856
+ def run_to_user_code(self) -> str:
857
+ return self.custom_post({"interface": "RunToUserCode", "params": {}})
858
+
859
+ @check_server_available
860
+ def debug_continue(self, status: int = 0) -> str:
861
+ return self.custom_post({"interface": "DebugContinue", "params": {"status": status}})
862
+
863
+ @check_server_available
864
+ def pause_all_threads(self) -> str:
865
+ return self.custom_post({"interface": "PauseAllThreads", "params": {}})
866
+
867
+ @check_server_available
868
+ def resume_all_threads(self) -> str:
869
+ return self.custom_post({"interface": "ResumeAllThreads", "params": {}})
870
+
871
+ @check_server_available
872
+ def thread_pause(self, tid: int) -> str:
873
+ return self.custom_post({"interface": "ThreadPause", "params": {"tid": tid}})
874
+
875
+ @check_server_available
876
+ def thread_resume(self, tid: int) -> str:
877
+ return self.custom_post({"interface": "ThreadResume", "params": {"tid": tid}})
878
+
879
+ @check_server_available
880
+ def animate_into(self, count: int = 3) -> str:
881
+ return self.custom_post({"interface": "AnimateInto", "params": {"count": count}})
882
+
883
+ @check_server_available
884
+ def animate_over(self, count: int = 3) -> str:
885
+ return self.custom_post({"interface": "AnimateOver", "params": {"count": count}})
886
+
887
+ @check_server_available
888
+ def animate_stop(self) -> str:
889
+ return self.custom_post({"interface": "AnimateStop", "params": {}})
890
+
891
+ # 统一入口类 把所有API聚合在一起
892
+ class BinSentryClient(
893
+ SystemApi,
894
+ LogConfigApi,
895
+ SymbolVarApi,
896
+ BreakPointApi,
897
+ DebugSessionApi,
898
+ RegisterThreadApi,
899
+ ModulePeApi,
900
+ MemoryApi,
901
+ DisasmXrefApi,
902
+ StackTraceApi,
903
+ ExecutionControlApi
904
+ ):
905
+ def __init__(self, config: Optional[Config] = None):
906
+ super().__init__(config)