iotsploit-core 0.0.6__py3-none-any.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.
Files changed (41) hide show
  1. iotsploit_core/__init__.py +7 -0
  2. iotsploit_core/context.py +27 -0
  3. iotsploit_core/core/__init__.py +0 -0
  4. iotsploit_core/core/base_plugin.py +290 -0
  5. iotsploit_core/core/device_config.py +105 -0
  6. iotsploit_core/core/device_manager.py +942 -0
  7. iotsploit_core/core/device_registry.py +118 -0
  8. iotsploit_core/core/device_scanner.py +74 -0
  9. iotsploit_core/core/device_spec.py +105 -0
  10. iotsploit_core/core/device_store.py +86 -0
  11. iotsploit_core/core/execution_backend.py +464 -0
  12. iotsploit_core/core/execution_queue.py +456 -0
  13. iotsploit_core/core/exploit_manager.py +728 -0
  14. iotsploit_core/core/exploit_spec.py +73 -0
  15. iotsploit_core/core/stream_manager.py +157 -0
  16. iotsploit_core/core/tool_config.py +430 -0
  17. iotsploit_core/core/tool_manager.py +1166 -0
  18. iotsploit_core/core/tool_service.py +936 -0
  19. iotsploit_core/domain/__init__.py +3 -0
  20. iotsploit_core/domain/device.py +70 -0
  21. iotsploit_core/domain/execution_plan.py +29 -0
  22. iotsploit_core/domain/plugin.py +17 -0
  23. iotsploit_core/domain/stream.py +63 -0
  24. iotsploit_core/domain/target.py +243 -0
  25. iotsploit_core/platforms/__init__.py +19 -0
  26. iotsploit_core/platforms/consts.py +25 -0
  27. iotsploit_core/ports/__init__.py +6 -0
  28. iotsploit_core/ports/driver_state_repo.py +15 -0
  29. iotsploit_core/ports/plugin_repo.py +26 -0
  30. iotsploit_core/ports/stream_backend.py +28 -0
  31. iotsploit_core/ports/task_runner.py +22 -0
  32. iotsploit_core/ports/wifi_backend.py +148 -0
  33. iotsploit_core/py.typed +2 -0
  34. iotsploit_core/utils/__init__.py +53 -0
  35. iotsploit_core/utils/exceptions.py +152 -0
  36. iotsploit_core/utils/helpers.py +65 -0
  37. iotsploit_core/utils/iots_logger.py +164 -0
  38. iotsploit_core/utils/result.py +41 -0
  39. iotsploit_core-0.0.6.dist-info/METADATA +117 -0
  40. iotsploit_core-0.0.6.dist-info/RECORD +41 -0
  41. iotsploit_core-0.0.6.dist-info/WHEEL +4 -0
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ __all__ = ["__version__"]
4
+
5
+ __version__ = "0.1.0"
6
+
7
+
@@ -0,0 +1,27 @@
1
+ """
2
+ Plugin Context for Backend Injection.
3
+
4
+ This module provides a typed context object for injecting platform-specific
5
+ backends into plugins. This avoids magic string keys and provides better
6
+ type safety and IDE support.
7
+ """
8
+
9
+ from dataclasses import dataclass
10
+ from typing import Optional
11
+
12
+ from iotsploit_core.ports.wifi_backend import WifiBackend
13
+
14
+
15
+ @dataclass
16
+ class PluginContext:
17
+ """
18
+ Context object for injecting platform-specific backends into plugins.
19
+
20
+ This provides a structured way to pass backends to plugins, avoiding
21
+ dictionary-based injection with magic string keys. As new backends are
22
+ added, they can be added as optional fields here.
23
+
24
+ Attributes:
25
+ wifi: Optional WiFi backend instance
26
+ """
27
+ wifi: Optional[WifiBackend] = None
File without changes
@@ -0,0 +1,290 @@
1
+ import threading
2
+ import logging
3
+ from typing import Dict, Any, List, Optional
4
+ from iotsploit_core.core.stream_manager import StreamManager, StreamWrapper
5
+ from iotsploit_core.domain.device import Device
6
+ from iotsploit_core.core.device_spec import DeviceState
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ class BasePlugin:
11
+ def __init__(self, info: Dict[str, Any] = None):
12
+ self.info = info or {}
13
+
14
+ def update_info(self, new_info: Dict[str, Any]):
15
+ self.info.update(new_info)
16
+
17
+ def get_info(self) -> Dict[str, Any]:
18
+ return self.info
19
+
20
+ class BaseDeviceDriver(BasePlugin):
21
+ """Base class for device drivers implementing the DevicePluginSpec interface"""
22
+
23
+ def __init__(self, info: Dict[str, Any] = None):
24
+ super().__init__(info)
25
+ # Device management
26
+ self.device = None
27
+ self._devices: Dict[str, Device] = {}
28
+
29
+ # Stream management (core-safe: StreamManager is a facade that falls back to Noop if
30
+ # Django/Channels is unavailable).
31
+ self.stream_manager = StreamManager()
32
+ self.stream_wrapper = StreamWrapper(self.stream_manager)
33
+
34
+ # Acquisition management
35
+ self.acquisition_thread = None
36
+ self.is_acquiring = threading.Event()
37
+
38
+ # 确保 supported_commands 总是存在
39
+ if not hasattr(self, 'supported_commands'):
40
+ self.supported_commands = {} # format: {'command': 'description'}
41
+
42
+ def get_supported_commands(self) -> Dict[str, str]:
43
+ """Get dictionary of supported commands and their descriptions"""
44
+ if not hasattr(self, 'supported_commands'):
45
+ return {}
46
+ return self.supported_commands
47
+
48
+ # Base implementations of device lifecycle methods
49
+ def scan(self) -> List[Device]:
50
+ """Scan for available devices"""
51
+ try:
52
+ devices = self._scan_impl()
53
+ for device in devices:
54
+ self._register_device(device)
55
+ return devices
56
+ except Exception as e:
57
+ logger.error(f"Scan failed: {str(e)}")
58
+ raise
59
+
60
+ def initialize(self, device: Device) -> bool:
61
+ """Initialize device"""
62
+ try:
63
+ return self._initialize_impl(device)
64
+ except Exception as e:
65
+ logger.error(f"Initialization failed: {str(e)}")
66
+ raise
67
+
68
+ def connect(self, device: Device) -> bool:
69
+ """Connect to device"""
70
+ try:
71
+ return self._connect_impl(device)
72
+ except Exception as e:
73
+ logger.error(f"Connection failed: {str(e)}")
74
+ raise
75
+
76
+ def command(self, device: Device, command: str, args: Optional[Dict] = None) -> Optional[str]:
77
+ """Execute command"""
78
+ try:
79
+ return self._command_impl(device, command, args)
80
+ except Exception as e:
81
+ logger.error(f"Command execution failed: {str(e)}")
82
+ raise
83
+
84
+ def reset(self, device: Device) -> bool:
85
+ """Reset device"""
86
+ try:
87
+ return self._reset_impl(device)
88
+ except Exception as e:
89
+ logger.error(f"Reset failed: {str(e)}")
90
+ raise
91
+
92
+ def close(self, device: Device) -> bool:
93
+ """Close device"""
94
+ try:
95
+ return self._close_impl(device)
96
+ except Exception as e:
97
+ logger.error(f"Close failed: {str(e)}")
98
+ raise
99
+
100
+ def recovery(self, device: Device, recovery_type: str, **kwargs) -> dict:
101
+ """
102
+ Execute recovery operations on device
103
+
104
+ Args:
105
+ device: Device to perform recovery on
106
+ recovery_type: Type of recovery operation (e.g., 'flash_firmware', 'openocd_attach')
107
+ **kwargs: Additional parameters for recovery operation
108
+
109
+ Returns:
110
+ dict: Recovery operation result with standardized format
111
+ """
112
+ try:
113
+ logger.info(f"Starting recovery operation '{recovery_type}' on device {device.device_id}")
114
+ result = self._recovery_impl(device, recovery_type, **kwargs)
115
+
116
+ # Ensure standardized response format
117
+ if not isinstance(result, dict):
118
+ result = {"status": "error", "message": "Invalid response format from recovery implementation"}
119
+
120
+ # Add standard fields if missing
121
+ if "status" not in result:
122
+ result["status"] = "unknown"
123
+ if "message" not in result:
124
+ result["message"] = f"Recovery operation '{recovery_type}' completed"
125
+
126
+ logger.info(f"Recovery operation '{recovery_type}' completed with status: {result.get('status')}")
127
+ return result
128
+
129
+ except NotImplementedError:
130
+ return {
131
+ "status": "error",
132
+ "message": f"Recovery operation '{recovery_type}' not supported by this driver"
133
+ }
134
+ except Exception as e:
135
+ logger.error(f"Recovery operation failed: {str(e)}")
136
+ return {
137
+ "status": "error",
138
+ "message": f"Recovery operation failed: {str(e)}"
139
+ }
140
+
141
+ def get_supported_recovery_operations(self) -> list:
142
+ """
143
+ Get list of supported recovery operations for this driver
144
+
145
+ Returns:
146
+ list: List of supported recovery operation names
147
+ """
148
+ try:
149
+ return self._get_supported_recovery_operations_impl()
150
+ except NotImplementedError:
151
+ return []
152
+ except Exception as e:
153
+ logger.error(f"Error getting supported recovery operations: {str(e)}")
154
+ return []
155
+
156
+ # Streaming and acquisition control
157
+ def start_streaming(self, device: Device):
158
+ """启动设备数据流(包括数据采集和WebSocket分发)"""
159
+ try:
160
+ logger.info(f"Starting streaming for device {device.device_id}")
161
+ self.stream_wrapper.register_stream(device.device_id)
162
+ self.start_acquisition(device)
163
+ except Exception as e:
164
+ logger.error(f"Failed to start streaming: {e}")
165
+ self.stop_streaming(device)
166
+ raise
167
+
168
+ def stop_streaming(self, device: Device):
169
+ """停止设备数据流(包括数据采集和WebSocket分发)"""
170
+ try:
171
+ logger.info(f"Stopping streaming for device {device.device_id}")
172
+ self.stop_acquisition(device)
173
+ self.stream_wrapper.unregister_stream(device.device_id)
174
+ self.stream_wrapper.stop_broadcast(device.device_id)
175
+ except Exception as e:
176
+ logger.error(f"Error stopping streaming: {e}")
177
+ raise
178
+
179
+ def start_acquisition(self, device: Device):
180
+ """启动设备数据采集(不包括WebSocket分发)"""
181
+ try:
182
+ logger.info(f"Starting data acquisition for device {device.device_id}")
183
+ self._setup_acquisition(device)
184
+ if not self.is_acquiring.is_set():
185
+ self.is_acquiring.set()
186
+ self.acquisition_thread = threading.Thread(
187
+ target=self._acquisition_loop,
188
+ name=f'{self.__class__.__name__}_Acquisition'
189
+ )
190
+ self.acquisition_thread.daemon = True
191
+ self.acquisition_thread.start()
192
+ except Exception as e:
193
+ logger.error(f"Failed to start acquisition: {e}")
194
+ self.stop_acquisition(device)
195
+ raise
196
+
197
+ def stop_acquisition(self, device: Device):
198
+ """停止设备数据采集(不包括WebSocket分发)"""
199
+ logger.info(f"Stopping data acquisition for device {device.device_id}")
200
+ self.is_acquiring.clear()
201
+ if self.acquisition_thread and self.acquisition_thread.is_alive():
202
+ self.acquisition_thread.join(timeout=1.0)
203
+ self.acquisition_thread = None
204
+ try:
205
+ self._cleanup_acquisition(device)
206
+ except Exception as e:
207
+ logger.error(f"Error in acquisition cleanup: {e}")
208
+ raise
209
+
210
+ # Methods to be implemented by derived classes
211
+ def _scan_impl(self) -> List[Device]:
212
+ """Implementation of device scanning"""
213
+ raise NotImplementedError
214
+
215
+ def _initialize_impl(self, device: Device) -> bool:
216
+ """Implementation of device initialization"""
217
+ raise NotImplementedError
218
+
219
+ def _connect_impl(self, device: Device) -> bool:
220
+ """Implementation of device connection"""
221
+ raise NotImplementedError
222
+
223
+ def _command_impl(self, device: Device, command: str, args: Optional[Dict] = None) -> Optional[str]:
224
+ """Implementation of command execution"""
225
+ raise NotImplementedError
226
+
227
+ def _reset_impl(self, device: Device) -> bool:
228
+ """Implementation of device reset"""
229
+ raise NotImplementedError
230
+
231
+ def _close_impl(self, device: Device) -> bool:
232
+ """Implementation of device closure"""
233
+ raise NotImplementedError
234
+
235
+ def _recovery_impl(self, device: Device, recovery_type: str, **kwargs) -> dict:
236
+ """
237
+ Implementation of recovery operations
238
+
239
+ Args:
240
+ device: Device to perform recovery on
241
+ recovery_type: Type of recovery operation
242
+ **kwargs: Additional parameters
243
+
244
+ Returns:
245
+ dict: Recovery result with format:
246
+ {
247
+ "status": "success|error|warning",
248
+ "message": "Human readable message",
249
+ "execution_time": float, # optional
250
+ "details": dict # optional additional details
251
+ }
252
+ """
253
+ raise NotImplementedError
254
+
255
+ def _get_supported_recovery_operations_impl(self) -> list:
256
+ """
257
+ Implementation of supported recovery operations listing
258
+
259
+ Returns:
260
+ list: List of supported recovery operation names
261
+ """
262
+ raise NotImplementedError
263
+
264
+ def _setup_acquisition(self, device: Device):
265
+ """设备特定的采集初始化"""
266
+ pass
267
+
268
+ def _cleanup_acquisition(self, device: Device):
269
+ """设备特定的采集清理"""
270
+ pass
271
+
272
+ def _acquisition_loop(self):
273
+ """数据采集循环的具体实现"""
274
+ raise NotImplementedError
275
+
276
+ def get_device(self, device_id: str) -> Optional[Device]:
277
+ """获取设备实例"""
278
+ return self._devices.get(device_id)
279
+
280
+ def _register_device(self, device: Device):
281
+ """注册设备"""
282
+ self._devices[device.device_id] = device
283
+
284
+ def __del__(self):
285
+ """Cleanup when object is destroyed"""
286
+ try:
287
+ if self.device and hasattr(self, 'state') and self.state != DeviceState.DISCONNECTED:
288
+ self.close(self.device)
289
+ except Exception as e:
290
+ logger.error(f"Error during cleanup: {e}")
@@ -0,0 +1,105 @@
1
+ """
2
+ DEPRECATED: This module is deprecated and will be removed in a future version.
3
+
4
+ Device configuration is now managed entirely through the SQLite database.
5
+ Use an application-layer adapter (e.g. Django/SQLAlchemy) instead.
6
+
7
+ For importing devices from JSON, use the 'device_import' CLI command.
8
+ """
9
+ import os
10
+ import json
11
+ import warnings
12
+ from typing import Dict, Optional
13
+ import logging
14
+ from iotsploit_core.domain.device import Device, DeviceType
15
+ from json import JSONEncoder
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # Emit deprecation warning when module is imported
20
+ warnings.warn(
21
+ "device_config module is deprecated. Device configuration is now managed through the database. "
22
+ "Use an application-layer adapter (e.g. Django/SQLAlchemy) instead.",
23
+ DeprecationWarning,
24
+ stacklevel=2
25
+ )
26
+
27
+ class DeviceJSONEncoder(JSONEncoder):
28
+ """自定义JSON编码器,处理特殊类型的序列化
29
+
30
+ DEPRECATED: This class is deprecated. Use database storage instead.
31
+ """
32
+ def default(self, obj):
33
+ if isinstance(obj, DeviceType):
34
+ return obj.value
35
+ if isinstance(obj, Device):
36
+ # 使用 DeviceStore 中的序列化方法
37
+ from iotsploit_core.core.device_store import DeviceStore
38
+ return DeviceStore()._device_to_dict(obj)
39
+ return super().default(obj)
40
+
41
+ class DeviceConfigManager:
42
+ """设备配置管理器
43
+
44
+ DEPRECATED: This class is deprecated.
45
+ Device configuration is now managed entirely through the SQLite database.
46
+ Use an application-layer adapter (e.g. Django/SQLAlchemy) for device operations.
47
+ """
48
+
49
+ def __init__(self):
50
+ warnings.warn(
51
+ "DeviceConfigManager is deprecated. Use an application-layer adapter (e.g. Django/SQLAlchemy) instead.",
52
+ DeprecationWarning,
53
+ stacklevel=2
54
+ )
55
+ self.config_file = "conf/devices.json"
56
+
57
+ def load_configs(self) -> Dict[str, Dict]:
58
+ """加载设备配置"""
59
+ if not os.path.exists(self.config_file):
60
+ return {"devices": []}
61
+
62
+ try:
63
+ with open(self.config_file, 'r') as f:
64
+ return json.load(f)
65
+ except json.JSONDecodeError as e:
66
+ logger.error(f"Error decoding JSON from {self.config_file}: {e}")
67
+ return {"devices": []}
68
+ except Exception as e:
69
+ logger.error(f"Error loading config file {self.config_file}: {e}")
70
+ return {"devices": []}
71
+
72
+ def save_device_config(self, device_dict: Dict):
73
+ """保存设备配置到持久化存储"""
74
+ try:
75
+ configs = self.load_configs()
76
+ # 确保configs中有devices键
77
+ if 'devices' not in configs:
78
+ configs['devices'] = []
79
+
80
+ # 更新或添加设备配置
81
+ device_id = device_dict['device_id']
82
+ device_updated = False
83
+ for i, device in enumerate(configs['devices']):
84
+ if device.get('device_id') == device_id:
85
+ configs['devices'][i] = device_dict
86
+ device_updated = True
87
+ break
88
+
89
+ if not device_updated:
90
+ configs['devices'].append(device_dict)
91
+
92
+ os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
93
+ with open(self.config_file, 'w') as f:
94
+ json.dump(configs, f, indent=2, cls=DeviceJSONEncoder)
95
+
96
+ except Exception as e:
97
+ logger.error(f"Error saving device configuration: {e}")
98
+
99
+ def get_device_config(self, device_id: str) -> Optional[Dict]:
100
+ """获取特定设备的配置"""
101
+ configs = self.load_configs()
102
+ for device in configs.get('devices', []):
103
+ if device.get('device_id') == device_id:
104
+ return device
105
+ return None