├── Dependencies-Installation ├── README.md └── Vol-GUI /Dependencies-Installation: -------------------------------------------------------------------------------- 1 | sudo apt-get install python3-tk git python2-dev 2 | wget https://bootstrap.pypa.io/pip/2.7/get-pip.py 3 | sudo python2 get-pip.py 4 | pip2 install pycrypto distorm3 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vol-GUI 2 | 3 | https://user-images.githubusercontent.com/40142550/170536554-666f141f-7270-435f-b5cf-0ccbe0d652bd.mp4 4 | 5 | 6 | A few things to note: 7 | 1) When you clicked a button, it will be pressed until its execution is done. 8 | 2) If you did scannings before, now again you wanna analyse (Dump, Look) the output files. So no need to scan again, **but make sure you click on 'Check Profile' once after you select your dump file via 'Choose File' button**, so that it will be used for searching, dumping files etc etc 9 | 3) Sometimes you can encounter blank pop ups, that can happen when no data or specific output is available. 10 | 4) Files are saved with the naming convention- pluginName_output.txt inside the memory dump file folder created as memdumpFile_analysis 11 | 12 | PS I m not a very good coder (hehe 🥴) so if you face any issues during runtime or have any suggestions or need some more functionality implemented please feel free contact me on Twitter 13 | -------------------------------------------------------------------------------- /Vol-GUI: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import subprocess 3 | import os, re, itertools 4 | from tkinter import * 5 | from tkinter import filedialog, messagebox 6 | from tkinter.constants import SUNKEN 7 | import tkinter.font as font 8 | 9 | #If profile is not checked and other button is clicked make sure they check profile first by prompting messagebox. 10 | 11 | #Check if volatility exists or not. 12 | if not os.path.exists('volatility/vol.py'): 13 | print("Cloning Volatility") 14 | os.system("git clone https://github.com/volatilityfoundation/volatility") 15 | print("Installing Volatility") 16 | os.system('cd volatility; sudo python2 setup.py install;cd ..') 17 | print("Installed Volatility") 18 | print("Adding Plugins") 19 | os.system("git clone https://github.com/superponible/volatility-plugins plugins") 20 | print("Installed Plugins") 21 | 22 | #LineBreak 23 | def linebreak(): 24 | ourMessage ='----' 25 | messageVar = Message(master, text = ourMessage) 26 | messageVar.config() 27 | messageVar.pack() 28 | 29 | #Opening Dump File 30 | def browseFiles(): 31 | global filename 32 | global fpath 33 | filename = filedialog.askopenfilename(initialdir = "~",title = "Select Dump File", 34 | filetypes = (("All Files","*.*"), ("Text Files",".txt*"))) 35 | location=filename.split('/') 36 | file = location[-1] 37 | file = str(file) + '_analysis' 38 | del location[-1] 39 | located = ('/').join(location) 40 | fpath = os.path.join(located, file) 41 | 42 | answer = messagebox.askyesno(title='Confirmation', 43 | message='{}\nSure?'.format(filename)) 44 | if answer: 45 | if not os.path.isdir(fpath): 46 | print(' [+] Making Directory') 47 | os.mkdir(fpath) 48 | else: 49 | master.quit 50 | 51 | #Fetching Profile 52 | def profile_fetch(): 53 | global profile 54 | print('Checking Profile') 55 | f_name = str(fpath) + '/profile.txt' 56 | if not os.path.exists(f_name): 57 | cmd = "vol.py -f '{}' imageinfo".format(filename) 58 | profiledata= subprocess.getoutput(cmd) 59 | if 'ERROR' in profiledata: 60 | print('Error Occured') 61 | exit 62 | profile = (profiledata.split(':')[3]).split(',')[0].strip(' ') 63 | print(' [+] Writing Profile') 64 | f = open(f_name, "w") 65 | f.write(profile) 66 | f.close() 67 | else: 68 | f = open(f_name, "r") 69 | profile=f.readline() 70 | f.close() 71 | messagebox.showinfo("Profile Found", profile) 72 | 73 | #Do not populate data when scan output is there. 74 | def execute(): 75 | if var1.get(): 76 | single_arg_scan('pslist') 77 | if var2.get(): 78 | single_arg_scan('cmdscan') 79 | single_arg_scan('consoles') 80 | if var3.get(): 81 | single_arg_scan('filescan') 82 | if var4.get(): 83 | single_arg_scan('envars') 84 | if var5.get(): 85 | single_arg_scan('clipboard') 86 | if var6.get(): 87 | single_arg_scan('netscan') 88 | single_arg_scan('sockscan') 89 | if var7.get(): 90 | single_arg_scan('mftparser') 91 | if var8.get(): 92 | single_arg_scan('iehistory') 93 | if var9.get(): 94 | single_arg_scan('services') 95 | if var10.get(): 96 | single_arg_scan('hashdump') 97 | if var11.get(): 98 | single_arg_scan('dlllist') 99 | if var12.get(): 100 | populate_data() 101 | populate_data() 102 | 103 | #Executing Commands which requires single argument. 104 | def single_arg_scan(plugin): 105 | if plugin == 'iehistory': 106 | cmd = "cat '{}'/pslist_output.txt".format(fpath) 107 | temp = subprocess.getoutput(cmd) 108 | if 'chrome.exe' in str(temp): 109 | try: 110 | print(' [+] Starting chromehistory Scan') 111 | cmd1 = "vol.py --plugins=plugins -f '{}' chromehistory".format(filename) 112 | plugindata= subprocess.getoutput(cmd1) 113 | if len(plugindata) > 1: 114 | f_name = str(fpath) + '/chromehistory_output.txt' 115 | print(' [+] Writing Output Of chromehistory Plugin') 116 | f = open(f_name, "w") 117 | f.write(plugindata) 118 | f.close() 119 | except: 120 | print('[!] Error Occured in chromehistory scan') 121 | print('[!] Please run "{}" command from current directory.'.format(cmd1)) 122 | if 'firefox.exe' in str(temp): 123 | print(' [+] Starting firefoxhistory Scan') 124 | cmd1 = "vol.py --plugins=plugins -f '{}' firefoxhistory".format(filename) 125 | plugindata= subprocess.getoutput(cmd1) 126 | if len(plugindata) > 1: 127 | f_name = str(fpath) + '/firefoxhistory_output.txt' 128 | print(' [+] Writing Output Of firefoxhistory Plugin') 129 | f = open(f_name, "w") 130 | f.write(plugindata) 131 | f.close() 132 | print(' [+] Starting {} Scan'.format(plugin)) 133 | cmd2 = 'vol.py -f "{}" --profile={} {}'.format(filename,profile, plugin) 134 | plugindata= subprocess.getoutput(cmd2) 135 | if len(plugindata) > 1: 136 | f_name = str(fpath) + '/{}_output.txt'.format(plugin) 137 | print(' [+] Writing Output Of {} Plugin'.format(plugin)) 138 | f = open(f_name, "w") 139 | f.write(plugindata) 140 | f.close() 141 | else: 142 | print('[!] No output for {} Plugin'.format(plugin)) 143 | print('[+] Written') 144 | 145 | #Dumping Process/Memory based on PID 146 | def dumper(plugin,pid): 147 | print(' [+] Starting {} Scan'.format(plugin)) 148 | cmd = "vol.py --profile={} {} --pid={} --dump-dir='{}' -f '{}'".format(profile,plugin,pid,fpath,filename) 149 | temp = subprocess.run(cmd, shell=True,capture_output=True,text=True, check=True) 150 | if 'OK' or 'Writing' in str(temp): 151 | print(' [+] Dumped {} File'.format(pid)) 152 | else: 153 | print('[!] File Not Available') 154 | 155 | #Dumping file based on offset (-Q) 156 | def dumper_file(offset, f_name): 157 | print(' [+] Starting dumpfiles Scan') 158 | cmd = "vol.py --profile={} dumpfiles -n --dump-dir='{}' -Q {} -f '{}'".format(profile,fpath, offset,filename) 159 | temp = subprocess.run(cmd, shell=True,capture_output=True,text=True, check=True) 160 | if 'ImageSectionObject' or 'DataSectionObject' in str(temp): 161 | print(' [+] Dumped ' +f_name+ 'File') 162 | else: 163 | print('[!] File Not Available') 164 | 165 | #Executing Dumper based on selection 166 | def showSelected(typ): 167 | if str(typ) == "file": 168 | dmpFile = my_list2.get(ANCHOR) 169 | showMsg = 'Dumping: '+ dmpFile.split(",")[0] 170 | print(showMsg) 171 | #show.config(text=showMsg) 172 | getOffset = dmpFile.split(":")[1] 173 | dumper_file(getOffset, dmpFile.split(",")[0]) 174 | 175 | elif str(typ) == "proc": 176 | dmpFile = my_list2.get(ANCHOR) 177 | showMsg = 'Dumping: '+ dmpFile 178 | #show.config(text=showMsg) 179 | print(showMsg) 180 | getPID = dmpFile.split(':')[2] 181 | print('Dumping PID:',getPID) 182 | dumper('procdump',getPID ) 183 | elif str(typ) == "mem": 184 | dmpFile = my_list2.get(ANCHOR) 185 | showMsg = 'Dumping: '+ dmpFile 186 | #show.config(text=showMsg) 187 | print(showMsg) 188 | getPID = dmpFile.split(':')[2] 189 | print('Dumping PID:',getPID) 190 | dumper('memdump', getPID) 191 | 192 | #Data parsing of process list 193 | def proc_list(): 194 | cmd1= "cat '{}'/pslist_output.txt |tail -n +4 > '{}'/pslist.txt".format(fpath, fpath) 195 | os.system(cmd1) 196 | fname= str(fpath)+'/pslist.txt' 197 | file1 = open(fname, 'r') 198 | Lines = file1.readlines() 199 | count = 0 200 | processlist=[] 201 | for line in Lines: 202 | count += 1 203 | newline = line.strip() 204 | s = ' '.join(newline.split()) 205 | chunks = s.split(' ') 206 | entry = 'Process:'+chunks[1]+' & PID:'+ chunks[2] 207 | processlist.append(entry) 208 | return processlist 209 | 210 | #Data parsing of process list 211 | def file_list(): 212 | cmd2 = "cat '{}'/filescan_output.txt | tail -n +4 > '{}'/filescan.txt".format(fpath, fpath) 213 | os.system(cmd2) 214 | fname= str(fpath)+'/filescan.txt' 215 | file1 = open(fname, 'r') 216 | Lines = file1.readlines() 217 | flist=[] 218 | desktop_filelist=[] 219 | downloads_filelist=[] 220 | documents_filelist=[] 221 | temp_filelist=[] 222 | music_filelist=[] 223 | video_filelist=[] 224 | picture_filelist=[] 225 | for line in Lines: 226 | newline = line.strip() 227 | s = ' '.join(newline.split()) 228 | chunks = s.split(' ') 229 | offset = chunks.pop(0) 230 | chunks.pop(0) 231 | chunks.pop(0) 232 | chunks.pop(0) 233 | file_name = ' '.join(chunks) 234 | entry = offset+', FileName: '+ file_name 235 | if re.search(r'\\Desktop\\' , entry ): 236 | desktop_filelist.append(entry) 237 | if re.search(r'\\Documents\\' , entry ): 238 | documents_filelist.append(entry) 239 | if re.search(r'\\Downloads\\' , entry ): 240 | downloads_filelist.append(entry) 241 | if re.search(r'\\Temp\\' , entry ): 242 | temp_filelist.append(entry) 243 | if re.search(r'\\Pictures\\' , entry ): 244 | picture_filelist.append(entry) 245 | if re.search(r'\\Videos\\' , entry ): 246 | video_filelist.append(entry) 247 | if re.search(r'\\Music\\' , entry ): 248 | music_filelist.append(entry) 249 | flist=list(itertools.chain(desktop_filelist, documents_filelist, downloads_filelist,temp_filelist,picture_filelist,video_filelist,music_filelist )) 250 | return flist 251 | 252 | def populate_data(): 253 | global dump_files 254 | global process_dump 255 | global mem_dump 256 | global cmdinfo 257 | global hashdmp 258 | global browse_hist 259 | process_dump = proc_list() 260 | mem_dump = proc_list() 261 | dump_files = file_list() 262 | 263 | def cmd_info(): 264 | populate_data() 265 | global my_list4 266 | newWindow = Toplevel(master) 267 | cmd1 = "cat '{}'/cmdscan_output.txt | tail -n +2 > '{}'/cmdscan.txt".format(fpath, fpath) 268 | os.system(cmd1) 269 | newWindow.geometry("700x150") 270 | newWindow.title("Cmd Prompt Info") 271 | my_list3 = Listbox(newWindow,height = 6,width = 55) 272 | my_list3.pack() 273 | hfile = "{}/cmdscan.txt".format(fpath) 274 | f = open(hfile,'r') 275 | Lines = f.readlines() 276 | for line in Lines: 277 | newline = line.strip() 278 | if 'Cmd #' in newline: 279 | up_line = newline.split(': ') 280 | if len(up_line[-1]) < 3 or '????' in up_line[-1] or 'Cmd #' in up_line[-1]: 281 | pass 282 | else: 283 | my_list3.insert(END, up_line[-1]) 284 | 285 | def hash_dump(): 286 | populate_data() 287 | global my_list3 288 | newWindow = Toplevel(master) 289 | cmd1 = 'cat "{}/hashdump_output.txt" | tail -n +2 > "{}/hashdump.txt"'.format(fpath, fpath) 290 | os.system(cmd1) 291 | newWindow.geometry("700x150") 292 | newWindow.title("Hashdump Info") 293 | my_list3 = Listbox(newWindow,height = 6,width = 55) 294 | my_list3.pack() 295 | hfile = '{}/hashdump.txt'.format(fpath) 296 | f = open(hfile,'r') 297 | Lines = f.readlines() 298 | count = 0 299 | for line in Lines: 300 | count += 1 301 | newline = line.strip() 302 | my_list3.insert(END, newline) 303 | 304 | def browser_history(): 305 | populate_data() 306 | #Note- chrome,firefox history plugin dont need profile 307 | global my_list1 308 | newWindow = Toplevel(master) 309 | newWindow.title("Browser History") 310 | # sets the geometry of toplevel 311 | newWindow.geometry("600x200") 312 | my_list1 = Listbox(newWindow,height = 10,width = 65) 313 | my_list1.pack() 314 | urllist= [] 315 | if os.path.exists(str(fpath)+'/chromehistory_output.txt'): 316 | cmd = 'cat "{}/chromehistory_output.txt" | tail -n +4 > "{}/chromehistory.txt"'.format(fpath,fpath) 317 | os.system(cmd) 318 | fname= '{}/chromehistory.txt'.format(fpath) 319 | file1 = open(fname, 'r') 320 | Lines = file1.readlines() 321 | count = 0 322 | for line in Lines: 323 | count += 1 324 | newline = line.strip() 325 | s = ','.join(newline.split()) 326 | chunks = s.split(',') 327 | entry = 'URL: '+chunks[1] 328 | urllist.append(entry) 329 | if os.path.exists(str(fpath)+'/firefoxhistory_output.txt'): 330 | cmd = "cat '{}'/firefoxhistory_output.txt | tail -n +4 > '{}'/firefoxhistory.txt".format(fpath,fpath) 331 | os.system(cmd) 332 | fname= "{}/firefoxhistory.txt".format(fpath) 333 | file1 = open(fname, 'r') 334 | Lines = file1.readlines() 335 | count = 0 336 | for line in Lines: 337 | count += 1 338 | newline = line.strip() 339 | s = ','.join(newline.split()) 340 | chunks = s.split(',') 341 | entry = 'URL: '+chunks[1] 342 | urllist.append(entry) 343 | if os.path.exists(str(fpath)+'/iehistory_output.txt'): 344 | cmd = "cat '{}'/iehistory_output.txt | grep Visited | cut -d '@' -f 2 > '{}'/iehistory.txt".format(fpath,fpath) 345 | os.system(cmd) 346 | fname= "{}/iehistory.txt".format(fpath) 347 | file1 = open(fname, 'r') 348 | Lines = file1.readlines() 349 | count = 0 350 | for line in Lines: 351 | entry = 'URL: '+line 352 | urllist.append(entry) 353 | for lnk in urllist: 354 | my_list1.insert(END, lnk) 355 | 356 | def dumpFileWindow(typ1): 357 | populate_data() 358 | global my_list2 359 | newWindow = Toplevel(master) 360 | 361 | # sets the geometry of toplevel 362 | newWindow.geometry("600x220") 363 | my_list2 = Listbox(newWindow,height = 9,width = 55) 364 | my_list2.pack() 365 | if str(typ1)=="file": 366 | newWindow.title("Dump Files") 367 | for item in dump_files: 368 | my_list2.insert(END, str(item.split("\\")[-4])+str("\\")+str(item.split("\\")[-3])+str("\\")+str(item.split("\\")[-2])+str("\\")+str(item.split("\\")[-1])+' , Offset:'+str(item.split(",")[0])) 369 | if str(typ1)=="proc": 370 | newWindow.title("Dump Process") 371 | for item in process_dump: 372 | my_list2.insert(END, item) 373 | if str(typ1)=="mem": 374 | newWindow.title("Dump Memory") 375 | for item in mem_dump: 376 | my_list2.insert(END, item) 377 | buttonExample = Button(newWindow, text = "Dump File",command=lambda:showSelected(typ1)) 378 | buttonExample.pack() 379 | 380 | def showInfoWindow(typ1): 381 | global my_list2 382 | newWindow = Toplevel(master) 383 | # sets the geometry of toplevel 384 | newWindow.geometry("600x200") 385 | my_list2 = Listbox(newWindow,height = 9,width = 55) 386 | my_list2.pack() 387 | if str(typ1)=="cmd": 388 | for item in dump_files: 389 | my_list2.insert(END, str(item.split("\\")[-4])+str("\\")+str(item.split("\\")[-3])+str("\\")+str(item.split("\\")[-2])+str("\\")+str(item.split("\\")[-1])+' , Offset:'+str(item.split(",")[0])) 390 | if str(typ1)=="history": 391 | for item in process_dump: 392 | my_list2.insert(END, item) 393 | if str(typ1)=="hash": 394 | for item in mem_dump: 395 | my_list2.insert(END, item) 396 | buttonExample = Button(newWindow, text = "Dump File",command=lambda:showSelected(typ1)) 397 | buttonExample.pack() 398 | 399 | #Searching 400 | def findtext(): 401 | found = 0 402 | tosearch = modify.get() 403 | print(' [+] Searching {}'.format(tosearch)) 404 | for root,d_names,f_names in os.walk(fpath): 405 | filenames=f_names 406 | for i in filenames: 407 | ff = str(fpath)+'/'+str(i) 408 | if '_output.txt' in ff: 409 | myfile = open(ff, "r") 410 | flag = 0 411 | index = 0 412 | for line in myfile: 413 | index += 1 414 | if tosearch in line: 415 | flag = 1 416 | break 417 | if flag == 0: 418 | pass 419 | else: 420 | print(' [+] String', tosearch, 'Found In Line', index, 'in', i) 421 | found = 1 422 | myfile.close() 423 | if found != 1: 424 | print('[!] {} Not Available'.format(tosearch)) 425 | 426 | # Setting up GUI 427 | master = Tk() 428 | master.option_add('*Font', '25') 429 | master.title("Vol-GUI by D3v1LaL") 430 | master.geometry("550x910") 431 | menu = Menu(master) 432 | master.config(menu=menu) 433 | filemenu = Menu(menu) 434 | 435 | myFont = font.Font(size=12, weight='bold') 436 | 437 | button = Button(master, text='Choose File', width=25, command=lambda:browseFiles()) 438 | button['font'] = myFont 439 | button.pack(ipady=7) 440 | 441 | linebreak() 442 | 443 | button = Button(master, text='Check Profile', width=25, command=lambda:profile_fetch()) 444 | button['font'] = myFont 445 | button.pack(ipady=7) 446 | 447 | linebreak() 448 | 449 | frame = LabelFrame(master, text='Select Profile', padx=22, bg= '#dddddd') 450 | frame['font'] = myFont 451 | frame.pack() 452 | var1 = IntVar() 453 | Checkbutton(frame, text="Process Scan",variable=var1, onvalue=1 ).pack(anchor=W) 454 | var2 = IntVar() 455 | Checkbutton(frame, text="Command Line Scan",variable=var2, onvalue=2).pack(anchor=W) 456 | var3 = IntVar() 457 | Checkbutton(frame, text="File Scan",variable=var3, onvalue=3).pack(anchor=W) 458 | var4 = IntVar() 459 | Checkbutton(frame, text="Environment Variables", variable=var4, onvalue=4).pack(anchor=W) 460 | var5 = IntVar() 461 | Checkbutton(frame, text="Clipboard", variable=var5, onvalue=4).pack(anchor=W) 462 | var6 = IntVar() 463 | Checkbutton(frame, text="Network Connections", variable=var6, onvalue=4).pack(anchor=W) 464 | var7 = IntVar() 465 | Checkbutton(frame, text="Master File Table", variable=var7, onvalue=4).pack(anchor=W) 466 | var8 = IntVar() 467 | Checkbutton(frame, text="Browser History", variable=var8, onvalue=4).pack(anchor=W) 468 | var9 = IntVar() 469 | Checkbutton(frame, text="Services", variable=var9, onvalue=4).pack(anchor=W) 470 | var10 = IntVar() 471 | Checkbutton(frame, text="Hash Dump", variable=var10, onvalue=4).pack(anchor=W) 472 | var11 = IntVar() 473 | Checkbutton(frame, text="Dll List", variable=var11, onvalue=4).pack(anchor=W) 474 | var12 = IntVar() 475 | Checkbutton(frame, text="Populate Data", variable=var12, onvalue=4).pack(anchor=W) 476 | 477 | button = Button(master, text='Execute', command=lambda:execute() ,padx=20, pady=5) 478 | button['font'] = myFont 479 | button.pack(pady=10) 480 | 481 | linebreak() 482 | 483 | #Searching Part 484 | Frm = Frame(master) 485 | Label(Frm,text='Enter to search:').pack(side=LEFT) 486 | modify = Entry(Frm) 487 | modify.pack(side=LEFT, fill=BOTH, expand=1) 488 | modify.focus_set() 489 | buttn = Button(Frm, text='Search',command=lambda:findtext()) 490 | buttn['font'] = myFont 491 | buttn.pack(side=RIGHT) 492 | Frm.pack(side=TOP) 493 | 494 | btn = Button(master, 495 | text ="Dump Files", 496 | command = lambda:dumpFileWindow('file')) 497 | btn['font'] = myFont 498 | btn.pack(pady = 10) 499 | 500 | btn1 = Button(master, 501 | text ="Dump Process", 502 | command = lambda:dumpFileWindow('proc')) 503 | btn1['font'] = myFont 504 | btn1.pack(pady = 10) 505 | 506 | btn2 = Button(master, 507 | text ="Dump Memory", 508 | command = lambda:dumpFileWindow('mem')) 509 | btn2['font'] = myFont 510 | btn2.pack(pady = 10) 511 | 512 | btn3 = Button(master, 513 | text ="Browser History", 514 | command = lambda:browser_history()) 515 | btn3['font'] = myFont 516 | btn3.pack(pady = 10) 517 | 518 | btn4 = Button(master, 519 | text ="Hashdump Info", 520 | command = lambda:hash_dump()) 521 | btn4['font'] = myFont 522 | btn4.pack(pady = 10) 523 | 524 | btn5 = Button(master, 525 | text ="Cmd Prompt Info", 526 | command = lambda:cmd_info()) 527 | btn5['font'] = myFont 528 | btn5.pack(pady = 10) 529 | 530 | # Let the window wait for any events 531 | mainloop() 532 | --------------------------------------------------------------------------------