multivol 0.1.3__py3-none-any.whl → 0.1.4__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.
- multivol/api.py +678 -191
- multivol/multi_volatility2.py +27 -173
- multivol/multi_volatility3.py +91 -104
- multivol/multivol.py +118 -42
- multivol/strings.py +71 -0
- {multivol-0.1.3.dist-info → multivol-0.1.4.dist-info}/METADATA +1 -1
- multivol-0.1.4.dist-info/RECORD +12 -0
- multivol-0.1.3.dist-info/RECORD +0 -11
- {multivol-0.1.3.dist-info → multivol-0.1.4.dist-info}/WHEEL +0 -0
- {multivol-0.1.3.dist-info → multivol-0.1.4.dist-info}/entry_points.txt +0 -0
- {multivol-0.1.3.dist-info → multivol-0.1.4.dist-info}/licenses/LICENSE +0 -0
- {multivol-0.1.3.dist-info → multivol-0.1.4.dist-info}/top_level.txt +0 -0
multivol/multivol.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# multivol.py
|
|
2
2
|
# Entry point for MultiVolatility: orchestrates running Volatility2 and Volatility3 memory analysis in parallel using multiprocessing.
|
|
3
3
|
import multiprocessing, time, os, argparse, sys
|
|
4
|
+
import docker
|
|
5
|
+
from datetime import datetime
|
|
4
6
|
from rich.console import Console
|
|
5
7
|
from rich.theme import Theme
|
|
6
8
|
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn, TimeElapsedColumn
|
|
@@ -9,9 +11,16 @@ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskPr
|
|
|
9
11
|
try:
|
|
10
12
|
from .multi_volatility2 import multi_volatility2
|
|
11
13
|
from .multi_volatility3 import multi_volatility3
|
|
14
|
+
from .strings import get_strings
|
|
12
15
|
except ImportError:
|
|
13
16
|
from multi_volatility2 import multi_volatility2
|
|
14
17
|
from multi_volatility3 import multi_volatility3
|
|
18
|
+
from strings import get_strings
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Wrapper for Volatility 3 to use with imap
|
|
23
|
+
|
|
15
24
|
|
|
16
25
|
# Wrapper for Volatility 3 to use with imap
|
|
17
26
|
def vol3_wrapper(packed_args):
|
|
@@ -29,14 +38,17 @@ def runner(arguments):
|
|
|
29
38
|
if not arguments.light and not arguments.full:
|
|
30
39
|
arguments.light = True
|
|
31
40
|
|
|
41
|
+
if hasattr(arguments, "output_dir") and arguments.output_dir:
|
|
42
|
+
output_dir = arguments.output_dir
|
|
43
|
+
elif hasattr(arguments, "output") and arguments.output:
|
|
44
|
+
output_dir = arguments.output
|
|
45
|
+
else:
|
|
46
|
+
output_dir = f"output_{datetime.now().strftime('%Y_%m_%d_%H_%M_%S')}"
|
|
47
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
48
|
+
|
|
32
49
|
# Handle Volatility2 mode
|
|
33
50
|
if arguments.mode == "vol2":
|
|
34
51
|
volatility2_instance = multi_volatility2()
|
|
35
|
-
if hasattr(arguments, "output_dir") and arguments.output_dir:
|
|
36
|
-
output_dir = arguments.output_dir
|
|
37
|
-
else:
|
|
38
|
-
output_dir = f"volatility2_{os.path.basename(arguments.dump)}__output"
|
|
39
|
-
os.makedirs(output_dir, exist_ok=True)
|
|
40
52
|
# Determine commands to run based on arguments
|
|
41
53
|
if arguments.commands:
|
|
42
54
|
commands = arguments.commands.split(",")
|
|
@@ -54,11 +66,6 @@ def runner(arguments):
|
|
|
54
66
|
# Handle Volatility3 mode
|
|
55
67
|
elif arguments.mode == "vol3":
|
|
56
68
|
volatility3_instance = multi_volatility3()
|
|
57
|
-
if hasattr(arguments, "output_dir") and arguments.output_dir:
|
|
58
|
-
output_dir = arguments.output_dir
|
|
59
|
-
else:
|
|
60
|
-
output_dir = f"volatility3_{os.path.basename(arguments.dump)}__output"
|
|
61
|
-
os.makedirs(output_dir, exist_ok=True)
|
|
62
69
|
# Determine commands to run based on arguments
|
|
63
70
|
if arguments.commands:
|
|
64
71
|
commands = arguments.commands.split(",")
|
|
@@ -68,7 +75,10 @@ def runner(arguments):
|
|
|
68
75
|
else:
|
|
69
76
|
commands = volatility3_instance.getCommands("windows.full")
|
|
70
77
|
elif arguments.linux:
|
|
71
|
-
|
|
78
|
+
if arguments.light:
|
|
79
|
+
commands = volatility3_instance.getCommands("linux.light")
|
|
80
|
+
else:
|
|
81
|
+
commands = volatility3_instance.getCommands("linux.full")
|
|
72
82
|
|
|
73
83
|
# Limit the number of parallel processes
|
|
74
84
|
# Default to len(commands) (unlimited) if processes arg is not set or None
|
|
@@ -85,6 +95,38 @@ def runner(arguments):
|
|
|
85
95
|
|
|
86
96
|
custom_theme = Theme({"info": "dim cyan", "warning": "magenta", "danger": "bold red"})
|
|
87
97
|
console = Console(theme=custom_theme)
|
|
98
|
+
|
|
99
|
+
# Docker Image Check & Pull
|
|
100
|
+
try:
|
|
101
|
+
client = docker.from_env()
|
|
102
|
+
image_name = arguments.image
|
|
103
|
+
# Check if --image was passed in args. rudimentary check.
|
|
104
|
+
user_provided_image = "--image" in sys.argv
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
client.images.get(image_name)
|
|
108
|
+
if not user_provided_image:
|
|
109
|
+
console.print(f"[dim cyan][*] No --image provided, using default image: {image_name}[/dim cyan]")
|
|
110
|
+
except docker.errors.ImageNotFound:
|
|
111
|
+
msg = f"[*] No --image provided, pulling default image: {image_name}" if not user_provided_image else f"[*] Pulling image: {image_name}"
|
|
112
|
+
|
|
113
|
+
# Use Progress with custom columns to put spinner at the end
|
|
114
|
+
with Progress(
|
|
115
|
+
TextColumn("{task.description}"),
|
|
116
|
+
SpinnerColumn("dots"),
|
|
117
|
+
transient=True,
|
|
118
|
+
console=console
|
|
119
|
+
) as progress:
|
|
120
|
+
progress.add_task(f"[bold green]{msg}[/bold green]", total=None)
|
|
121
|
+
client.images.pull(image_name)
|
|
122
|
+
|
|
123
|
+
# Re-print the message so it persists in the log
|
|
124
|
+
console.print(f"[bold green]{msg}[/bold green]")
|
|
125
|
+
console.print(f"[bold green][*] Image {image_name} ready.[/bold green]")
|
|
126
|
+
except Exception as e:
|
|
127
|
+
console.print(f"[bold red]Warning: Docker check failed: {e}[/bold red]")
|
|
128
|
+
# We don't exit here, we let the individual/pool commands fail if they must, or maybe user has local setup issues.
|
|
129
|
+
|
|
88
130
|
console.print("\n[bold green][+] Launching all commands...[/bold green]\n")
|
|
89
131
|
|
|
90
132
|
# Use multiprocessing Manager for Lock
|
|
@@ -106,35 +148,47 @@ def runner(arguments):
|
|
|
106
148
|
arguments.format,
|
|
107
149
|
False, # quiet
|
|
108
150
|
lock, # lock
|
|
109
|
-
arguments.host_path
|
|
151
|
+
arguments.host_path,
|
|
152
|
+
getattr(arguments, "debug", False)
|
|
110
153
|
) for cmd in commands]
|
|
111
154
|
)
|
|
155
|
+
# Vol2 Starmap doesn't use wrapper, so we can't easily report running/completed per module unless we wrap it too.
|
|
156
|
+
# For now we focus on Vol3
|
|
157
|
+
# But we should probably fix Vol2 too.
|
|
158
|
+
# TODO: Add wrapper for Vol2 or update vol2 logic.
|
|
112
159
|
else:
|
|
160
|
+
|
|
113
161
|
# Enforce priority execution for Info module to ensure symbols are downloaded/cached
|
|
114
162
|
if arguments.windows:
|
|
115
163
|
info_module = "windows.info.Info"
|
|
116
164
|
else:
|
|
117
165
|
info_module = "linux.bash.Bash"
|
|
118
|
-
if arguments.windows or
|
|
119
|
-
commands
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
166
|
+
if arguments.windows or arguments.linux:
|
|
167
|
+
if info_module in commands:
|
|
168
|
+
commands.remove(info_module)
|
|
169
|
+
volatility3_instance.execute_command_volatility3(info_module,
|
|
170
|
+
os.path.basename(arguments.dump),
|
|
171
|
+
os.path.abspath(arguments.dump),
|
|
172
|
+
arguments.symbols_path,
|
|
173
|
+
arguments.image,
|
|
174
|
+
os.path.abspath(arguments.cache_path),
|
|
175
|
+
os.path.abspath(arguments.plugins_dir),
|
|
176
|
+
output_dir,
|
|
177
|
+
arguments.format,
|
|
178
|
+
False, # quiet
|
|
179
|
+
lock, # lock
|
|
180
|
+
arguments.host_path,
|
|
181
|
+
True if getattr(arguments, "fetch_symbol", False) else False,
|
|
182
|
+
getattr(arguments, "debug", False),
|
|
183
|
+
getattr(arguments, "custom_symbol", None),
|
|
184
|
+
getattr(arguments, "scan_id", None)
|
|
185
|
+
)
|
|
134
186
|
|
|
135
187
|
|
|
136
188
|
# Prepare arguments for imap
|
|
137
189
|
# We must pass the instance because wrapper is global and doesn't see local variable
|
|
190
|
+
# Signature: command, dump, dump_dir, symbols_path, docker_image, cache_dir, plugin_dir, output_dir, format,
|
|
191
|
+
# quiet=False, lock=None, host_path=None, fetch_symbols=False, show_commands=False, custom_symbol=None, scan_id=None
|
|
138
192
|
tasks_args = [(volatility3_instance, (cmd,
|
|
139
193
|
os.path.basename(arguments.dump),
|
|
140
194
|
os.path.abspath(arguments.dump),
|
|
@@ -144,10 +198,13 @@ def runner(arguments):
|
|
|
144
198
|
os.path.abspath(arguments.plugins_dir),
|
|
145
199
|
output_dir,
|
|
146
200
|
arguments.format,
|
|
147
|
-
False,
|
|
148
|
-
lock,
|
|
201
|
+
False, # quiet=False so we see the output as it happens
|
|
202
|
+
lock, # lock
|
|
149
203
|
arguments.host_path,
|
|
150
|
-
|
|
204
|
+
getattr(arguments, "fetch_symbol", False), # fetch_symbols
|
|
205
|
+
getattr(arguments, "debug", False), # show_commands
|
|
206
|
+
getattr(arguments, "custom_symbol", None), # custom_symbol
|
|
207
|
+
getattr(arguments, "scan_id", None) # scan_id
|
|
151
208
|
)) for cmd in commands]
|
|
152
209
|
|
|
153
210
|
# Progress counters
|
|
@@ -168,6 +225,19 @@ def runner(arguments):
|
|
|
168
225
|
failed_modules.append(command_name)
|
|
169
226
|
if arguments.format == "json":
|
|
170
227
|
console.print(f"[red][!] Failed to validate JSON for {command_name}[/red]")
|
|
228
|
+
|
|
229
|
+
console.print("\n[+] Starting strings...")
|
|
230
|
+
|
|
231
|
+
get_strings(
|
|
232
|
+
os.path.basename(arguments.dump),
|
|
233
|
+
os.path.abspath(arguments.dump),
|
|
234
|
+
output_dir,
|
|
235
|
+
arguments.image,
|
|
236
|
+
lock,
|
|
237
|
+
arguments.host_path
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
console.print("\n[+] Strings complete !")
|
|
171
241
|
|
|
172
242
|
console.print(f"\n[bold green]Scan Complete![/bold green] Success: {success_count}, Failed: {failed_count}")
|
|
173
243
|
|
|
@@ -187,7 +257,7 @@ def runner(arguments):
|
|
|
187
257
|
|
|
188
258
|
def main():
|
|
189
259
|
# Argument parsing for CLI usage
|
|
190
|
-
parser = argparse.ArgumentParser("
|
|
260
|
+
parser = argparse.ArgumentParser("multivol")
|
|
191
261
|
parser.add_argument("--api", action="store_true", help="Start API server")
|
|
192
262
|
parser.add_argument("--dev", action="store_true", help="Enable developer mode (hot reload)")
|
|
193
263
|
parser.add_argument("--host-path", type=str, required=False, default=None, help="Root path of the project on the Host machine (required for Docker-in-Docker)")
|
|
@@ -198,7 +268,7 @@ def main():
|
|
|
198
268
|
vol2_parser.add_argument("--profiles-path", help="Path to the directory with the profiles.", default=os.path.join(os.getcwd(), "volatility2_profiles"))
|
|
199
269
|
vol2_parser.add_argument("--profile", help="Profile to use.", required=True)
|
|
200
270
|
vol2_parser.add_argument("--dump", help="Dump to parse.", required=True)
|
|
201
|
-
vol2_parser.add_argument("--image", help="Docker image to use.", required=
|
|
271
|
+
vol2_parser.add_argument("--image", help="Docker image to use.", required=False, default="sp00kyskelet0n/volatility2")
|
|
202
272
|
vol2_parser.add_argument("--commands", help="Commands to run : command1,command2,command3", required=False)
|
|
203
273
|
vol2_os_group = vol2_parser.add_mutually_exclusive_group(required=True)
|
|
204
274
|
vol2_os_group.add_argument("--linux", action="store_true", help="For a Linux memory dump")
|
|
@@ -206,24 +276,31 @@ def main():
|
|
|
206
276
|
vol2_parser.add_argument("--light", action="store_true", help="Use the main modules.")
|
|
207
277
|
vol2_parser.add_argument("--full", action="store_true", help="Use all modules.")
|
|
208
278
|
vol2_parser.add_argument("--format", help="Format of the outputs: json, text", required=False, default="text")
|
|
209
|
-
vol2_parser.add_argument("--processes", type=int, required=False, default=None, help="Max number of concurrent processes")
|
|
279
|
+
vol2_parser.add_argument("--processes", type=int, required=False, default=None, help="Max number of concurrent processes.")
|
|
280
|
+
vol2_parser.add_argument("--output", required=False, help="Directory where outputs will be written (Default: output_YYYY_MM_DD_HH_MM_SS).")
|
|
210
281
|
|
|
211
282
|
# Volatility3 argument group
|
|
212
283
|
vol3_parser = subparser.add_parser("vol3", help="Use volatility3.")
|
|
213
284
|
vol3_parser.add_argument("--dump", help="Dump to parse.", required=True)
|
|
214
|
-
vol3_parser.add_argument("--image", help="Docker image to use.", required=
|
|
285
|
+
vol3_parser.add_argument("--image", help="Docker image to use.", required=False, default="sp00kyskelet0n/volatility3")
|
|
215
286
|
vol3_parser.add_argument("--symbols-path", help="Path to the directory with the symbols.", required=False, default=os.path.join(os.getcwd(), "volatility3_symbols"))
|
|
216
287
|
vol3_parser.add_argument("--cache-path", help="Path to directory with the cache for volatility3.", required=False, default=os.path.join(os.getcwd(), "volatility3_cache"))
|
|
217
288
|
vol3_parser.add_argument("--plugins-dir", help="Path to directory with the plugins", required=False, default=os.path.join(os.getcwd(), "volatility3_plugins"))
|
|
218
289
|
vol3_parser.add_argument("--commands", help="Commands to run : command1,command2,command3", required=False)
|
|
219
290
|
vol3_os_group = vol3_parser.add_mutually_exclusive_group(required=True)
|
|
220
|
-
vol3_os_group.add_argument("--linux", action="store_true", help="It's a Linux memory dump")
|
|
221
|
-
vol3_os_group.add_argument("--windows", action="store_true", help="It's a Windows memory dump")
|
|
291
|
+
vol3_os_group.add_argument("--linux", action="store_true", help="It's a Linux memory dump.")
|
|
292
|
+
vol3_os_group.add_argument("--windows", action="store_true", help="It's a Windows memory dump.")
|
|
222
293
|
vol3_parser.add_argument("--light", action="store_true", help="Use the principal modules.")
|
|
223
294
|
vol3_parser.add_argument("--fetch-symbol", action="store_true", help="Fetch automatically symbol from github.com/Abyss-W4tcher/volatility3-symbols", required=False)
|
|
224
295
|
vol3_parser.add_argument("--full", action="store_true", help="Use all modules.")
|
|
225
296
|
vol3_parser.add_argument("--format", help="Format of the outputs: json, text", required=False, default="text")
|
|
226
297
|
vol3_parser.add_argument("--processes", type=int, required=False, default=None, help="Max number of concurrent processes")
|
|
298
|
+
vol3_parser.add_argument("--output", required=False, help="Directory where outputs will be written (Default: output_YYYY_MM_DD_HH_MM_SS).")
|
|
299
|
+
|
|
300
|
+
# Global arguments
|
|
301
|
+
parser.add_argument("--debug", action="store_true", help="Show executed Docker commands")
|
|
302
|
+
parser.add_argument("--scan-id", help="Scan UUID for API status updates", required=False)
|
|
303
|
+
|
|
227
304
|
args = parser.parse_args()
|
|
228
305
|
|
|
229
306
|
if args.api:
|
|
@@ -243,13 +320,12 @@ def main():
|
|
|
243
320
|
print("[-] --linux or --windows required.")
|
|
244
321
|
sys.exit(1)
|
|
245
322
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
print("[-] --linux not available with --full or --light")
|
|
323
|
+
if getattr(args, "fetch_symbol", False) and not args.linux:
|
|
324
|
+
print("[-] --fetch-symbol only available with --linux")
|
|
249
325
|
sys.exit(1)
|
|
250
326
|
|
|
251
|
-
if args.
|
|
252
|
-
print("[-] --fetch-symbol only available with
|
|
327
|
+
if args.mode == "vol2" and getattr(args, "fetch_symbol", False):
|
|
328
|
+
print("[-] --fetch-symbol only available with vol3")
|
|
253
329
|
sys.exit(1)
|
|
254
330
|
|
|
255
331
|
# Validate output format
|
multivol/strings.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import docker
|
|
2
|
+
import time
|
|
3
|
+
import os
|
|
4
|
+
from rich import print as rprint
|
|
5
|
+
|
|
6
|
+
def resolve_path(path, host_path):
|
|
7
|
+
# If host_path is set, replacing the current working directory prefix with host_path
|
|
8
|
+
if host_path:
|
|
9
|
+
if path.startswith("/storage"):
|
|
10
|
+
# Handle special storage mapping for Docker
|
|
11
|
+
# Map /storage -> {host_path}/storage/data
|
|
12
|
+
rel_path = os.path.relpath(path, "/storage")
|
|
13
|
+
return os.path.join(host_path, "storage", "data", rel_path)
|
|
14
|
+
|
|
15
|
+
if path.startswith(os.getcwd()):
|
|
16
|
+
rel_path = os.path.relpath(path, os.getcwd())
|
|
17
|
+
return os.path.join(host_path, rel_path)
|
|
18
|
+
return path
|
|
19
|
+
|
|
20
|
+
def safe_print(message, lock):
|
|
21
|
+
if lock:
|
|
22
|
+
with lock:
|
|
23
|
+
rprint(message)
|
|
24
|
+
else:
|
|
25
|
+
rprint(message)
|
|
26
|
+
|
|
27
|
+
def get_strings(dump, dump_dir, output_dir, docker_image, lock=False, host_path=None):
|
|
28
|
+
output_file = os.path.join(output_dir, f"strings_output.txt")
|
|
29
|
+
host_output_dir = resolve_path(os.path.abspath(output_dir), host_path)
|
|
30
|
+
|
|
31
|
+
host_dump_path = resolve_path(os.path.abspath(dump_dir), host_path)
|
|
32
|
+
host_dump_dir = os.path.dirname(host_dump_path)
|
|
33
|
+
|
|
34
|
+
volumes = {
|
|
35
|
+
host_dump_dir: {'bind': '/dump_dir', 'mode': 'ro'},
|
|
36
|
+
host_output_dir: {'bind': '/output', 'mode': 'rw'}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
dump_filename = os.path.basename(dump)
|
|
40
|
+
cmd_args = f"strings /dump_dir/{dump_filename}"
|
|
41
|
+
|
|
42
|
+
client = docker.from_env()
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
container = client.containers.run(
|
|
46
|
+
image=docker_image,
|
|
47
|
+
command=cmd_args,
|
|
48
|
+
volumes=volumes,
|
|
49
|
+
tty=True,
|
|
50
|
+
detach=True,
|
|
51
|
+
remove=False
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
with open(output_file, "wb") as file:
|
|
55
|
+
try:
|
|
56
|
+
for chunk in container.logs(stream=True):
|
|
57
|
+
file.write(chunk)
|
|
58
|
+
except Exception as log_err:
|
|
59
|
+
# Handle Docker log rotation errors
|
|
60
|
+
safe_print(f"[!] Log streaming interrupted: {log_err}, fetching remaining logs...", lock)
|
|
61
|
+
try:
|
|
62
|
+
remaining_logs = container.logs(stream=False)
|
|
63
|
+
file.write(remaining_logs)
|
|
64
|
+
except:
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
container.wait()
|
|
68
|
+
container.remove()
|
|
69
|
+
|
|
70
|
+
except Exception as e:
|
|
71
|
+
safe_print(f"[!] Error running strings: {e}", lock)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multivol
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: MultiVolatility: Analyze memory dumps faster than ever with Volatility2 and Volatility3 in parallel using Docker
|
|
5
5
|
Home-page: https://github.com/BoBNewz/MultiVolatility
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
multivol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
multivol/api.py,sha256=vk-v4yNxS0KF0FEegGixZxExtXXgouZNn_l_zxxyGB8,58903
|
|
3
|
+
multivol/multi_volatility2.py,sha256=rLM3RLzIf_b7EZiMKd1_8v2Q5K5WEJhmxwZVS74FMsE,4668
|
|
4
|
+
multivol/multi_volatility3.py,sha256=Pd20_d_9USVpT7pnu0cimGUOd1vx8A4PH9EiYceiR4I,9187
|
|
5
|
+
multivol/multivol.py,sha256=iIqklfEtPQbVMo21dS5PwUdT83Qoj8tSRYuH__Py02A,17176
|
|
6
|
+
multivol/strings.py,sha256=zh-oxtuhWkq_Qz-dh2Cp8nB1wCUWDv-LNaqKkftYFgo,2409
|
|
7
|
+
multivol-0.1.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
8
|
+
multivol-0.1.4.dist-info/METADATA,sha256=Qx4_etq0dM737SlZI64C9adunOvn1JwqbKttSrEL51I,4292
|
|
9
|
+
multivol-0.1.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
10
|
+
multivol-0.1.4.dist-info/entry_points.txt,sha256=FM4lUHzrKUmV37U6IemQkRGXEJdgyB7-dwtw1jgwTQc,52
|
|
11
|
+
multivol-0.1.4.dist-info/top_level.txt,sha256=DcxSP883XnM_ad5TXyCIZkzDcYSoI1bPTie_AzHlN0A,9
|
|
12
|
+
multivol-0.1.4.dist-info/RECORD,,
|
multivol-0.1.3.dist-info/RECORD
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
multivol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
multivol/api.py,sha256=N_7Sm8Ys_rjRI9MLm-ThTMcDXe6JJU1G11p6zljvQUs,37503
|
|
3
|
-
multivol/multi_volatility2.py,sha256=_Z2yxF05xLjzJSZBBisUEMT6WKy1YahbH4E-l41XvnI,9789
|
|
4
|
-
multivol/multi_volatility3.py,sha256=9jSfw5ti9TKTGO_tbeM4IaCJfufpNQoR_wILHF4Rmbs,9608
|
|
5
|
-
multivol/multivol.py,sha256=TI3y7jcC39XK3zxgwlKiOmTfOT69jzNDgbqf44p11Qc,13435
|
|
6
|
-
multivol-0.1.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
7
|
-
multivol-0.1.3.dist-info/METADATA,sha256=P_825nIZyAAIZlo-gXzPdjNpwdV-vK1UOXbybwQq_IY,4292
|
|
8
|
-
multivol-0.1.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
9
|
-
multivol-0.1.3.dist-info/entry_points.txt,sha256=FM4lUHzrKUmV37U6IemQkRGXEJdgyB7-dwtw1jgwTQc,52
|
|
10
|
-
multivol-0.1.3.dist-info/top_level.txt,sha256=DcxSP883XnM_ad5TXyCIZkzDcYSoI1bPTie_AzHlN0A,9
|
|
11
|
-
multivol-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|