├── EnScripts ├── Baseliner.EnScript ├── Differential EnScript v1.pdf └── Differential.EnScript ├── README ├── VT ├── hasher.EnScript └── virustotal.pl ├── chat └── YahooMessenger-Parser.EnScript ├── diffs └── strings_diff.diff ├── fresponse └── fresponse.bash ├── get_plugins_2.0.bsh ├── misc_other ├── getregs.pl └── mac.pl ├── misc_python ├── collect.py ├── create_cybox_demo.py ├── getIPInfo.py ├── getmalwaredomains.py ├── jobparser.py ├── mbr_parser.py ├── opswat.py ├── parsesummary.py ├── printkey.py ├── pushtimeline.py └── similar_files │ ├── setify.py │ └── stringify.bsh └── prefetch ├── pf_baseline.py └── prefetch_hash.py /EnScripts/Baseliner.EnScript: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Jamie Levy (Gleeda) jamie.levy@gmail.com 3 | * 4 | * Baseliner.EnScript 5 | * 6 | * Creates a SQLite database of Path, Hash and Size of all DLL files 7 | * in disk images from a created case. 8 | * 9 | * This program is free software; you can redistribute it and/or 10 | * modify it under the terms of the GNU General Public License 11 | * as published by the Free Software Foundation; either version 12 | * 2 of the License, or (at your option) any later version. 13 | */ 14 | 15 | include "GSI_LogLib" 16 | include "GSI_Basic" 17 | 18 | class MainClass { 19 | long RecordsAdded, 20 | RecordsFailed; 21 | String TableCreateQuery, 22 | InsertQuery, 23 | OutputPath; 24 | 25 | MainClass(): 26 | TableCreateQuery 27 | ( 28 | "CREATE TABLE entries(id INTEGER PRIMARY KEY ASC AUTOINCREMENT, path TEXT, hash TEXT, size INTEGER)" 29 | ), 30 | InsertQuery 31 | ( 32 | "INSERT INTO entries(path, hash, size) VALUES ('{0}', '{1}', '{2}')" 33 | ) 34 | { 35 | } 36 | void Main(CaseClass c) { 37 | LocalFileClass SQLiteFile(); 38 | SQLiteClass SQLiteData(); 39 | SystemClass::ClearConsole(SystemClass::SHOWCONSOLE); 40 | 41 | if (c) 42 | { 43 | NameListClass devices(); 44 | SearchClass search(); 45 | forall (DeviceClass d in c.DeviceRoot()) { 46 | Console.WriteLine("Device Name = "+d.Name()); 47 | Console.WriteLine("Device FullPath = "+d.FullPath()); 48 | new NameListClass (devices, d.FullPath()); 49 | } 50 | 51 | if (SystemClass::PathDialog(OutputPath, 52 | "Choose SQLite output file", 53 | "sqlite", 54 | "SQLite Files\t*.sqlite", 55 | SystemClass::CREATE)) 56 | { 57 | LocalFileClass SQLiteFile(); 58 | if (SQLiteFile.Open(OutputPath, FileClass::APPEND)) 59 | { 60 | if (SQLiteData.Open(SQLiteFile, SQLiteClass::SQLITE_OPEN_READWRITE)) 61 | { 62 | SQLiteClass::CommandClass command(); 63 | if (SQLiteData.CreateCommand(command)) 64 | { 65 | if (command.ExecuteNonQuery(TableCreateQuery)) 66 | Console.WriteLine("Table Created"); 67 | forall (EntryClass e in c.EntryRoot()) 68 | { 69 | if (e.Extension().Compare("dll") == 0) 70 | { 71 | String path = e.FullPath(); 72 | foreach (NameListClass d in devices){ 73 | path.Replace(c.Name() + "\\" + d.Name() + "\\", ""); 74 | } 75 | HashClass hash = search.ComputeHash(e); 76 | if(!hash.IsValid()) 77 | hash = "none"; 78 | if (command.ExecuteNonQuery(String::Format(InsertQuery, escapeQuotes(path), 79 | hash, 80 | e.LogicalSize()))) 81 | { 82 | Console.WriteLine(String::Format("Record added to database for '{0}'", e.Name())); 83 | ++RecordsAdded; 84 | } 85 | else 86 | { 87 | Console.WriteLine(String::Format("Couldn't add record: {0}", SystemClass::LastError())); 88 | ++RecordsFailed; 89 | } 90 | } 91 | } 92 | Console.WriteLine(String::Format("Records added: {0}", RecordsAdded)); 93 | Console.WriteLine(String::Format("Records that couldn't be added: {0}", RecordsFailed)); 94 | Console.WriteLine("Script finished"); 95 | } 96 | } 97 | } 98 | } 99 | } 100 | } 101 | 102 | String escapeQuotes(const String &input) 103 | { 104 | String retval = input; 105 | retval.Replace("'", "''"); 106 | return retval; 107 | } 108 | 109 | } 110 | 111 | -------------------------------------------------------------------------------- /EnScripts/Differential EnScript v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleeda/misc-scripts/03a0d9126359c6b4b0b508062d3422bea9b69036/EnScripts/Differential EnScript v1.pdf -------------------------------------------------------------------------------- /EnScripts/Differential.EnScript: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Jamie Levy 3 | * 4 | * Differential.EnScript 5 | * outputs files that are different from one disk image when compared to the original 6 | * 7 | */ 8 | class MainClass; 9 | 10 | class FileContainer: NameListClass { 11 | String myHash; 12 | int mySize; 13 | 14 | FileContainer(NameListClass parent, const String &name, const String &hash, int size): 15 | NameListClass(parent, name), 16 | myHash = hash, 17 | mySize = size 18 | { 19 | } 20 | 21 | void PrintWiki(LocalFileClass fileout, bool incl_hash, FileContainer other) { 22 | if (incl_hash && other == null) { 23 | Console.WriteLine("| New File | " + this.Name() + " | N/A | N/A | " + String(mySize) + " | " + String(myHash) + "|"); 24 | fileout.WriteLine("| New File | " + this.Name() + " | N/A | N/A | " + String(mySize) + " | " + String(myHash) + "|"); 25 | } else if (other == null) { 26 | Console.WriteLine("| New File | " + this.Name() + " | N/A | N/A | " + String(mySize) + " | N/A | "); 27 | fileout.WriteLine("| New File | " + this.Name() + " | N/A | N/A | " + String(mySize) + " | N/A | "); 28 | } else if (other != null && incl_hash) { 29 | fileout.WriteLine("| Size diff | " + this.Name() + " | " + String(other.mySize) + " | " + String(other.myHash) + " | " + String(mySize) + " | " + String(myHash)+ " | ") ; 30 | Console.WriteLine("| Size diff | " + this.Name() + " | " + String(other.mySize) + " | " + String(other.myHash) + " | " + String(mySize) + " | " + String(myHash) + " | ") ; 31 | } else if (other) { 32 | fileout.WriteLine("| Size diff | " + this.Name() + " | " + String(other.mySize) + " | N/A | " + String(mySize) + " | N/A | ") ; 33 | Console.WriteLine("| Size diff | " + this.Name() + " | " + String(other.mySize) + " | N/A | " + String(mySize) + " | N/A | ") ; 34 | } 35 | } 36 | 37 | void PrintCSV(LocalFileClass fileout, bool incl_hash, FileContainer other, const String &char = "") { 38 | if (incl_hash && other == null) { 39 | Console.WriteLine(" New File " + char + this.Name() + char + " N/A " + char + " N/A " + char + String(mySize) + char + String(myHash)); 40 | fileout.WriteLine(" New File " + char + this.Name() + char + " N/A " + char + " N/A " + char + String(mySize) + char + String(myHash)); 41 | } else if (other == null) { 42 | Console.WriteLine(" New File " + char + this.Name() + char + " N/A " + char + " N/A " + char + String(mySize) + char + " N/A "); 43 | fileout.WriteLine(" New File " + char + this.Name() + char + " N/A " + char + " N/A " + char + String(mySize) + char + " N/A "); 44 | } else if (other != null && incl_hash) { 45 | fileout.WriteLine(" Size diff " + char + this.Name() + char + String(other.mySize) + char + String(other.myHash) + char + String(mySize) + char + String(myHash)) ; 46 | Console.WriteLine(" Size diff " + char + this.Name() + char + String(other.mySize) + char + String(other.myHash) + char + String(mySize) + char + String(myHash)) ; 47 | } else if (other) { 48 | fileout.WriteLine(" Size diff " + char + this.Name() + char + String(other.mySize) + char + " N/A " + char + String(mySize) + char + " N/A ") ; 49 | Console.WriteLine(" Size diff " + char + this.Name() + char + String(other.mySize) + char + " N/A " + char + String(mySize) + char + " N/A ") ; 50 | } 51 | } 52 | 53 | void PrintPythonList(LocalFileClass fileout, bool incl_hash, FileContainer other, NameListClass devices, CaseClass c) { 54 | String name = this.Name(); 55 | foreach (NameListClass d in devices) { 56 | name.Replace(c.Name() + "\\" + d.Name() + "\\", ""); 57 | } 58 | name.Replace("\\", "\\\\"); 59 | if (other == null) { 60 | Console.WriteLine("NEW FILE: \"" + name + "\","); 61 | fileout.WriteLine(" \"" + name + "\","); 62 | } else if (!incl_hash && other != null) { 63 | Console.WriteLine("SIZE DIFF: " + String(other.mySize) + " | " + String(mySize) + " \"" + name + "\","); 64 | fileout.WriteLine(" \"" + name + "\","); 65 | } else if (incl_hash && other != null) { 66 | Console.WriteLine("SIZE/HASH DIFF: \"" + name + "\"" + String(other.mySize) + " | " +String(other.myHash) + " | " + String(mySize) + " | " + String(myHash)) ; 67 | fileout.WriteLine(" \"" + name + "\","); 68 | } 69 | } 70 | 71 | bool BadChar(String str) { 72 | //Check for bad characters 73 | forall (char ch in str) { 74 | if (!((ch >= 48 && ch <= 57) || (ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122) || ch == 45 || ch == 95)) 75 | return true; 76 | } 77 | return false; 78 | } 79 | } 80 | 81 | class MainClass { 82 | String before, 83 | after, 84 | char, 85 | listname; 86 | bool wiki_output, 87 | list_output, 88 | size_diff, 89 | blue_checked, 90 | hash, 91 | csv, 92 | utf8, 93 | ascii, 94 | tab; 95 | NameListClass devs; 96 | 97 | void Main(CaseClass c) { 98 | SystemClass::ClearConsole(SystemClass::SHOWCONSOLE); 99 | NameListClass devices = devs; 100 | 101 | LocalFileClass fileout(); 102 | String prev_disk = before, 103 | after_disk = after, 104 | OutputPath; 105 | bool wiki = wiki_output, 106 | list = list_output, 107 | incl_size = size_diff, 108 | selected = blue_checked, 109 | incl_hash = hash; 110 | FileContainer files1(null, "", "", 0), 111 | files2(null, "", "", 0); 112 | SearchClass search(); 113 | bool bad = files1.BadChar(listname); 114 | String ftype_ext = "txt", 115 | ftype = "Text Files\t*."; 116 | 117 | if (!wiki_output && !list_output && !csv && !tab) { 118 | return; 119 | } 120 | if (devices.Find(prev_disk) == null || devices.Find(after_disk) == null) { 121 | SystemClass::Message(SystemClass::ICONEXCLAMATION, "ERROR", "One of the evidence filenames written incorrectly"); 122 | return; 123 | } 124 | if (prev_disk == after_disk) { 125 | SystemClass::Message(SystemClass::ICONEXCLAMATION, "ERROR", "You must choose two different disks!"); 126 | return; 127 | } 128 | if (list && bad) { 129 | SystemClass::Message(SystemClass::ICONEXCLAMATION, "ERROR", "Invalid character written for Python list name!"); 130 | return; 131 | } 132 | if (list && listname == "") { 133 | SystemClass::Message(SystemClass::ICONEXCLAMATION, "ERROR", "Python list name left blank!"); 134 | return; 135 | } else if (list) { 136 | ftype_ext = "py"; 137 | ftype = "Python Code\t*."; 138 | } 139 | if (SystemClass::PathDialog(OutputPath, 140 | "Choose text output file", 141 | ftype_ext, 142 | ftype + ftype_ext, 143 | SystemClass::CREATE)) { 144 | if(! fileout.Open(OutputPath, FileClass::WRITETEXTCRLF)){ 145 | SystemClass::Message(SystemClass::ICONEXCLAMATION, "ERROR", "Output file cannot be created"); 146 | return; 147 | } 148 | if (utf8) 149 | fileout.SetCodePage(CodePageClass::UTF8); 150 | else if (ascii) 151 | fileout.SetCodePage(CodePageClass::ANSI); 152 | } 153 | else { 154 | SystemClass::Message(SystemClass::ICONEXCLAMATION, "ERROR", "No output file specified"); 155 | return; 156 | } 157 | 158 | forall (EntryClass e in c.EntryRoot()) { 159 | if (!e.IsFolder() && e.FullPath().Contains(prev_disk + "\\") && (!selected || e.IsSelected())) { 160 | HashClass hash; 161 | if (incl_hash) { 162 | hash = search.ComputeHash(e); 163 | } 164 | if(!incl_hash || !hash.IsValid()) 165 | hash = ""; 166 | String path = e.FullPath(); 167 | path.ToLower(); 168 | new FileContainer(files1, path, String(hash), e.LogicalSize()); 169 | } else if (! e.IsFolder() && e.FullPath().Contains(after_disk + "\\") && (!selected || e.IsSelected())) { 170 | HashClass hash; 171 | if (incl_hash) { 172 | hash = search.ComputeHash(e); 173 | } 174 | if(!incl_hash || !hash.IsValid()) 175 | hash = ""; 176 | new FileContainer(files2, e.FullPath(), hash, e.LogicalSize()); 177 | } 178 | } 179 | if (wiki) { 180 | Console.WriteLine("| Change | File | Original Size | Original Hash | New Size | New Hash |"); 181 | fileout.WriteLine("| Change | File | Original Size | Original Hash | New Size | New Hash |"); 182 | } 183 | else if (csv) { 184 | Console.WriteLine("Change" + char + "File" + char + "Original Size" + char + "Original Hash" + char + "New Size" + char + "New Hash"); 185 | fileout.WriteLine("Change" + char + "File" + char + "Original Size" + char + "Original Hash" + char + "New Size" + char + "New Hash"); 186 | } 187 | else if (tab) { 188 | Console.WriteLine(" Change\tFile\tOriginal Size\tOriginal Hash\tNew Size\tNew Hash"); 189 | fileout.WriteLine(" Change\tFile\tOriginal Size\tOriginal Hash\tNew Size\tNew Hash"); 190 | } 191 | else if (list) { 192 | Console.WriteLine(listname + " = [ "); 193 | fileout.WriteLine(listname + " = [ "); 194 | } 195 | foreach (FileContainer f2 in files2) { 196 | String path = f2.Name(); 197 | path.Replace(c.Name() + "\\" + after_disk + "\\", c.Name() + "\\" + prev_disk + "\\"); 198 | path.ToLower(); 199 | FileContainer temp = files1.Find(path); 200 | if (temp == null || ((incl_size || incl_hash) && (temp.mySize != f2.mySize || temp.myHash != f2.myHash))) { 201 | if (wiki) { 202 | f2.PrintWiki(fileout, incl_hash, temp); 203 | } 204 | else if (csv) { 205 | f2.PrintCSV(fileout, incl_hash, temp, char); 206 | } 207 | else if (tab) { 208 | f2.PrintCSV(fileout, incl_hash, temp, "\t"); 209 | } 210 | else if (list){ 211 | f2.PrintPythonList(fileout, incl_hash, temp, devices, c); 212 | } 213 | } 214 | else 215 | files1.Remove(temp); 216 | } 217 | if (list) { 218 | Console.WriteLine("]"); 219 | fileout.WriteLine("]"); 220 | } 221 | } 222 | 223 | class FilterDialogClass: DialogClass { 224 | CheckBoxClass wiki_output, 225 | list_output; 226 | StringEditClass list_name; 227 | CheckBoxClass tab, 228 | csv; 229 | StringEditClass char; 230 | CheckBoxClass size_diff, 231 | blue_checked, 232 | hash, 233 | utf8, 234 | ascii; 235 | ListEditClass ListOrig, ListDiff; 236 | MainClass Main; 237 | 238 | FilterDialogClass(DialogClass parent, MainClass v): 239 | Main = v, 240 | DialogClass(parent, "Options"), 241 | wiki_output(this, "Wiki Output", START, NEXT, 100, 12, 0, v.wiki_output), 242 | list_output(this, "Python List Output", START, NEXT, 100, 24, 0, v.list_output), 243 | list_name(this, "List Name", 120, SAME, 100, LEFT, 0, v.listname, 20, 0), 244 | tab(this, "TAB Delimted Output", START, NEXT, 100, 24, 0, v.tab), 245 | csv(this, "CSV Output", 120, SAME, 100, 24, 0, v.csv), 246 | char(this, "Character", 220, SAME, 10, LEFT, 0, v.char, 2, 0), 247 | size_diff(this, "Include size differences in output?", START, NEXT, 100, 12, 0, v.size_diff), 248 | blue_checked(this, "Selected files only?", START, NEXT, 100, 12, 0, v.blue_checked), 249 | hash(this, "Compare Hashes?", START, NEXT, 100, 12, 0, v.hash), 250 | utf8(this, "UTF-8?", START, NEXT, 100, 12, 0, v.utf8), 251 | ascii(this, "ASCII?", 110, SAME, 100, 12, 0, v.ascii), 252 | ListOrig(this, "Original Disk (Double Click To Select)", DEFAULT, NEXT, 500, 64, 0, v.devs, ListEditClass::CANVIEWNODES), 253 | ListDiff(this, "Differential Disk (Double Click To Select)", DEFAULT, NEXT, 500, 64, 0, v.devs, ListEditClass::CANVIEWNODES) 254 | { 255 | } 256 | virtual void CheckControls() { 257 | DialogClass::CheckControls(); 258 | EnableClose(wiki_output.GetValue() || list_output.GetValue() || csv.GetValue() || tab.GetValue()); 259 | } 260 | virtual void ChildEvent(const EventClass &evt) { 261 | DialogClass::ChildEvent(evt); 262 | if (ListOrig.Matches(evt) && evt.Type() == CHILDSELECTNODE) { 263 | NameListClass n = NameListClass::TypeCast(ListOrig.GetValue()); 264 | if (n) { 265 | Main.before = n.Name(); 266 | } 267 | } 268 | if (ListDiff.Matches(evt) && evt.Type() == CHILDSELECTNODE) { 269 | NameListClass n = NameListClass::TypeCast(ListDiff.GetValue()); 270 | if (n) { 271 | Main.after = n.Name(); 272 | } 273 | } 274 | if (wiki_output.Matches(evt)) { 275 | if (wiki_output.GetValue()) { 276 | list_output.Enable(false); 277 | char.Enable(false); 278 | csv.Enable(false); 279 | tab.Enable(false); 280 | list_name.Enable(false); 281 | } else { 282 | list_output.Enable(true); 283 | char.Enable(true); 284 | csv.Enable(true); 285 | tab.Enable(true); 286 | list_name.Enable(true); 287 | } 288 | } else if(list_output.Matches(evt)) { 289 | if ( list_output.GetValue()) { 290 | wiki_output.Enable(false); 291 | char.Enable(false); 292 | csv.Enable(false); 293 | tab.Enable(false); 294 | } else { 295 | wiki_output.Enable(true); 296 | char.Enable(true); 297 | csv.Enable(true); 298 | tab.Enable(true); 299 | } 300 | } else if(csv.Matches(evt)) { 301 | if (csv.GetValue()) { 302 | wiki_output.Enable(false); 303 | list_output.Enable(false); 304 | tab.Enable(false); 305 | list_name.Enable(false); 306 | } else { 307 | wiki_output.Enable(true); 308 | list_output.Enable(true); 309 | tab.Enable(true); 310 | list_name.Enable(true); 311 | } 312 | } else if(tab.Matches(evt)) { 313 | if (tab.GetValue()) { 314 | wiki_output.Enable(false); 315 | list_output.Enable(false); 316 | csv.Enable(false); 317 | char.Enable(false); 318 | list_name.Enable(false); 319 | } else { 320 | wiki_output.Enable(true); 321 | list_output.Enable(true); 322 | csv.Enable(true); 323 | char.Enable(true); 324 | list_name.Enable(true); 325 | } 326 | } else if(utf8.Matches(evt)) { 327 | if (utf8.GetValue()) { 328 | ascii.Enable(false); 329 | } else { 330 | ascii.Enable(true); 331 | } 332 | } else if(ascii.Matches(evt)) { 333 | if (ascii.GetValue()) { 334 | utf8.Enable(false); 335 | } else { 336 | utf8.Enable(true); 337 | } 338 | } 339 | } 340 | } 341 | 342 | MainClass() { 343 | String usageText = "This script will output the files that are different (size, hash, new) in one\n" 344 | "disk image compared to an original image. Good for pre/post infections to see what files changed\n\n" 345 | "Press OK to continue and cancel to exit\n"; 346 | int mbResponse = SystemClass::Message(SystemClass::MBOKCANCEL, "Differential", usageText); 347 | if (mbResponse == SystemClass::CANCEL) { 348 | return; 349 | } 350 | devs = new NameListClass(null, ""); 351 | forall (CaseClass cas in GlobalDataClass::CaseRoot()) { 352 | forall (DeviceClass d in cas.DeviceRoot()) { 353 | new NameListClass (devs, d.FullPath()); 354 | } 355 | } 356 | char = ","; 357 | FilterDialogClass dialog(null, this); 358 | if (dialog.Execute() != SystemClass::OK) 359 | return; 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gleeda/misc-scripts/03a0d9126359c6b4b0b508062d3422bea9b69036/README -------------------------------------------------------------------------------- /VT/hasher.EnScript: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Jamie Levy (Gleeda) 3 | * 4 | * hasher.EnScript 5 | * 6 | * Driver for virustotal.pl 7 | * 8 | * hashes selected files for case and calls virustotal.pl to 9 | * create an HTML report of these files 10 | * 11 | */ 12 | 13 | class MainClass { 14 | String hashfile, output; 15 | 16 | void Main(CaseClass c) { 17 | int notWorks, 18 | TotalFiles; 19 | String ActivePerl = "C:\\Perl\\bin"; 20 | String script = "C:\\Program Files\\EnCase6\\EnScript\\VT\\virustotal.pl"; 21 | String cmdline = "/c cd \"" + ActivePerl + "\" && perl "; 22 | 23 | 24 | if (!SystemClass::PathDialog(hashfile, 25 | "Choose Hash output file", 26 | "txt", 27 | "Text Files\t*.txt", 28 | SystemClass::CREATE)){ 29 | Console.WriteLine("Create Failed for: " + hashfile); 30 | return; 31 | } 32 | 33 | if (!SystemClass::FolderDialog(output, "VirusTotal Report :: Choose an Output Folder")){ 34 | Console.WriteLine("Create Failed for: " + output); 35 | return; 36 | } 37 | 38 | SystemClass::ClearConsole(SystemClass::SHOWCONSOLE); 39 | Console.WriteLine("HASHFILE: " + hashfile); 40 | cmdline += "\"" + script + "\" -t -d \"" + output + "\" -f " + "\"" + hashfile + "\""; 41 | 42 | SearchClass search(); 43 | LocalFileClass file(); 44 | file.Open(hashfile, FileClass::WRITETEXTCRLF); 45 | file.SetCodePage(CodePageClass::UTF8); 46 | 47 | ExecuteClass exec(); 48 | exec.SetApplication("cmd.exe"); 49 | exec.SetFolder("C:\\WINDOWS\\system32\\"); 50 | 51 | forall (EntryClass e in c.EntryRoot()) { 52 | if (e.IsSelected()) { 53 | ++TotalFiles; 54 | } 55 | } 56 | 57 | SystemClass::StatusRange("Hashing files", TotalFiles); 58 | 59 | forall (EntryClass e in c.EntryRoot()) { 60 | if (e.IsSelected()){ 61 | HashClass hash = search.ComputeHash(e); 62 | if (!hash.IsValid()) { 63 | notWorks++; 64 | Console.WriteLine("Rejected file: " + e.FullPath()); 65 | }else{ 66 | Console.WriteLine("Entry " + e.FullPath()); 67 | Console.WriteLine("Hash Value: " + hash); 68 | file.WriteLine(e.FullPath() + "\t" + hash); 69 | SystemClass::StatusInc(); 70 | } 71 | } 72 | } 73 | 74 | Console.WriteLine("Finished Hashing"); 75 | file.Close(); 76 | 77 | if (notWorks == 0) { 78 | Console.WriteLine("Successfully obtained hashes"); 79 | Console.WriteLine(cmdline); 80 | Console.WriteLine(exec.Folder()); 81 | Console.WriteLine(exec.Application()); 82 | 83 | exec.SetCommandLine(cmdline); 84 | exec.SetShow(true); //left this visible so we'll know when it finishes 85 | //exec.SetShow(false); //set this to false if you don't want to see the commandline 86 | if (exec.Start(LocalMachine, 1000)) { 87 | Console.WriteLine(exec.Output()); 88 | Console.WriteLine("Worked"); 89 | } 90 | else 91 | Console.WriteLine("Could Not Start Application"); 92 | } 93 | else 94 | Console.WriteLine("Did not obtain hashes"); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /VT/virustotal.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Author: Jamie Levy (Gleeda) 3 | # 4 | # virustotal.pl 5 | # 6 | # Takes in a tab separated hashfile (md5sum or sha1sum) or 7 | # traditional 2 space delimited hash file 8 | # prints out an html report from virustotal 9 | # 10 | # Updated 2010/12/7 to updated virustotal forms 11 | # 12 | 13 | 14 | # Create a user agent object 15 | use LWP::UserAgent; 16 | use Getopt::Long; 17 | use Cwd; 18 | use utf8; 19 | 20 | $usage = "usage: $0 \n\t-d \n\t-f \n\t-t (optional, tab delimited hashfile)\n"; 21 | &GetOptions("d=s", \$DIR, "f=s", \$FILE, "p=s", "t", \$TAB); 22 | 23 | $SIG{INT} = \&my_init_handler; 24 | 25 | die $usage unless defined $FILE; 26 | 27 | if( ! -e $FILE) { 28 | die "can't find file: $FILE\n$usage"; 29 | } 30 | 31 | if( defined $DIR ){ 32 | if( ! -d $DIR ){ 33 | mkdir $DIR or die; 34 | print "made directory: $DIR for output\n"; 35 | } 36 | }else{ 37 | $DIR = cwd; 38 | } 39 | 40 | %HASHES = (); 41 | &gethashes; 42 | &Chdir($DIR); 43 | 44 | open INDEX, ">:utf8", "index.html"; 45 | print INDEX "Virus Total Results for Case"; 46 | print INDEX "\n"; 47 | print INDEX "\n"; 48 | print INDEX "\n"; 49 | print INDEX "\n"; 51 | print INDEX "\n"; 53 | print INDEX "\n"; 55 | 56 | 57 | foreach $key (%HASHES) { 58 | if ($key =~ /[0-9a-fA-F]{32}/) { 59 | my @temp = split /::/, $HASHES{$key}; 60 | print "processing hash: $key for file $HASHES{$key}\n"; 61 | print INDEX ""; 66 | print INDEX "
\n"; 50 | print INDEX "File Name(s)\n"; 52 | print INDEX "Hash Value\n"; 54 | print INDEX "Percentage
\n"; 62 | foreach $h (@temp) { 63 | print INDEX "$h

\n"; 64 | } 65 | print INDEX "
\n"; 67 | &posthash($key); 68 | sleep (5); 69 | } 70 | } 71 | 72 | print INDEX "
\n"; 73 | 74 | close (INDEX); 75 | 76 | print "open index.html in the $DIR directory\n"; 77 | 78 | sub Chdir{ 79 | my $dir = shift; 80 | 81 | chdir $dir or 82 | die "unable to change to directory $dir\n"; 83 | } 84 | 85 | sub my_init_handler{ 86 | print INDEX "\n"; 87 | close (INDEX); 88 | die "index was written...\n"; 89 | } 90 | 91 | sub gethashes { 92 | open (INPUT, $FILE); 93 | binmode INPUT, ":utf8"; 94 | while () { 95 | chomp; 96 | s/^\s+//; 97 | s/\s+$//; 98 | my @temp; 99 | if (defined $TAB) { 100 | @temp = split /\t/; 101 | if (defined $HASHES{$temp[1]}) { 102 | $HASHES{$temp[1]} .= "::$temp[0]"; 103 | }else{ 104 | $HASHES{$temp[1]} = $temp[0]; 105 | } 106 | }else{ 107 | @temp = split / /; 108 | if (defined $HASHES{$temp[0]}) { 109 | $HASHES{$temp[0]} .= "::$temp[1]"; 110 | }else{ 111 | $HASHES{$temp[0]} = $temp[1]; 112 | } 113 | } 114 | 115 | 116 | } 117 | close (INPUT); 118 | } 119 | 120 | sub posthash { 121 | $HASH = @_[0]; 122 | open(OUT, ">$HASH.html"); 123 | print "processing $HASH\n"; 124 | $ua = LWP::UserAgent->new; 125 | $ua->agent("Mozilla/4.0"); 126 | $base = "http://www.virustotal.com"; 127 | 128 | # Create a request 129 | my $loc = $base . "/search.html"; 130 | print "posting to $loc\n"; 131 | my $req = HTTP::Request->new(POST => $loc); 132 | $req->content_type('application/x-www-form-urlencoded'); 133 | $req->content('chain=' . $HASH); 134 | print "HASH: $HASH\n"; 135 | 136 | # Pass request to the user agent and get a response back 137 | my $res = $ua->request($req); 138 | 139 | # Check the outcome of the response 140 | if ($res->is_redirect ) { 141 | $url = $res->header("Location"); 142 | 143 | print "getting from $url\n"; 144 | if ($url !~ /(report|id|analysis)/) { 145 | print INDEX "$HASH (not found)N/A\n"; 146 | close(OUT); 147 | unlink "$HASH.html"; 148 | } 149 | else { 150 | print INDEX "$HASH\n"; 151 | print INDEX "\n"; 152 | $req = HTTP::Request->new(GET => $url); 153 | $req->content_type('application/x-www-form-urlencoded'); 154 | $res = $ua->request($req); 155 | 156 | if ($res->is_success) { 157 | print OUT $res->content; 158 | $content = $res->content; 159 | if ($content =~ m/(.*?)<\/td>/ism) { 160 | $num = split /\/ [0-9][0-9]/, $1; 161 | if ($num > 0){ 162 | print INDEX "" . $num."<\/font><\/b>\/"; 163 | }else{ 164 | print INDEX "" . $num."<\/b>\/"; 165 | } 166 | @total_av = split /\//, $1; 167 | print INDEX $total_av[1]."\n"; 168 | } elsif ($content =~ m/(.*?)<\/td>/ism) { 169 | @total_av = split /\//, $1; 170 | print INDEX "0 / " . $total_av[1] . "\n"; 171 | } 172 | } 173 | else { 174 | print $res->status_line, "\n"; 175 | } 176 | } 177 | } 178 | else { 179 | print OUT "failed to retrieve hash\n"; 180 | } 181 | close (OUT); 182 | } 183 | -------------------------------------------------------------------------------- /chat/YahooMessenger-Parser.EnScript: -------------------------------------------------------------------------------- 1 | /* 2 | This script will parse the contents of an identified Yahoo Messenger chat log 3 | 4 | 1. Select all Yahoo Messenger chat log files 5 | 2. Type in the dialog box the screenname of the LOCAL user of the Messenger client (this screenname is case sensitive) 6 | 3. Output will be to the Log Records under bookmarks 7 | 4. Change line 25 to LogClass::DEBUG if you want all output displayed to the console 8 | 9 | Created by Paul Bobby, 2008 - paul.bobby@lmco.com 10 | */ 11 | 12 | /* 13 | * Added GUI to output chats directly to text files 14 | * 15 | * -gleeda 16 | */ 17 | 18 | include "GSI_LogLib" 19 | 20 | class MainClass { 21 | 22 | LogClass CLog; // Make CLog global so that it can be used throughout the script 23 | String variable; 24 | 25 | /* 26 | * Functions for handling RTL languages correctly 27 | * (added by Gleeda) 28 | */ 29 | bool WriteBuffer(MemoryFileClass &file, char msg) { 30 | file.SetCodePage(CodePageClass::ANSI); 31 | int temp = msg; 32 | file.WriteBinaryInt(temp, 1); 33 | return file.IsValid(); 34 | } 35 | 36 | void ReadBuffer(MemoryFileClass &file, String &msg) { 37 | file.SetCodePage(CodePageClass::UTF8); 38 | file.Seek(0); 39 | file.ReadString(msg); 40 | } 41 | 42 | 43 | void Main(CaseClass c) { 44 | // Start of Case startup code 45 | // 1. Check if a case is open with evidence added 46 | // 2. Clear the console and focus it 47 | // 3. Script start time 48 | SystemClass::ClearConsole(1); 49 | MemoryFileClass buffer(); 50 | String output; 51 | 52 | 53 | CLog = new LogClass("Yahoo Messenger Parser", LogClass::INFO, Console); 54 | if(!c){ 55 | CLog.Fatal("You must have an open case"); 56 | } 57 | if (!c.EntryRoot().FirstChild()) { 58 | CLog.Fatal("Please add some evidence to your case"); 59 | } 60 | 61 | DateClass now; 62 | now.Now(); 63 | uint start = now.GetUnix(); 64 | CLog.Info("Script Started"); 65 | // End of Case startup code 66 | 67 | // 68 | // Script specific variables 69 | 70 | String screenName = variable; 71 | if (!SystemClass::FolderDialog(output, "Chat output folder :: Choose an Output Folder")){ 72 | Console.WriteLine("Create Failed for: " + output); 73 | return; 74 | } 75 | 76 | long date; 77 | String xorText; 78 | char value, value2; 79 | int counter; 80 | bool direction; 81 | int screenNameSize = screenName.GetLength(); 82 | EntryFileClass ef(); 83 | BookmarkFolderClass parentFolder(c.BookmarkRoot(),"Yahoo Messenger Logs"); 84 | // 85 | // Script specific code start 86 | 87 | forall (EntryClass e in c.EntryRoot()) { 88 | if (e.IsSelected() && !e.IsFolder()) { 89 | Console.WriteLine(""); 90 | Console.WriteLine("++++++ Buddy: " + e.Parent().Name()); 91 | ExecuteClass exec(); 92 | exec.SetApplication("cmd.exe"); 93 | exec.SetFolder("C:\\WINDOWS\\system32\\"); 94 | String cmdline = " /c mkdir " + output + "\\" + screenName + "--" + e.Parent().Name(); 95 | exec.SetCommandLine(cmdline); 96 | exec.SetShow(true); 97 | 98 | if (exec.Start(LocalMachine, 1000)) { 99 | //Console.WriteLine(exec.Output()); 100 | } 101 | else 102 | Console.WriteLine("Could Not Start Application"); 103 | 104 | if (ef.Open(e,FileClass::NOUNERASE)) { 105 | CLog.Info("Parsing: " + e.FullPath()); 106 | LogRecordClass recs(); 107 | LogRecordClass rec(); 108 | long fileSize = ef.GetSize(); 109 | DateClass messageTime(); 110 | ef.SetCodePage(CodePageClass::ANSI); 111 | String folderName = parseParentFolder(e.FullPath()); 112 | 113 | while (ef.GetPos() < fileSize) { 114 | date = ef.ReadBinaryInt(4); 115 | messageTime.SetUnix(date); // Timestamp NOT adjusted for timezone 116 | ef.Skip(4); // Jump ahead 4 bytes (User type - unknown value) 117 | direction = ef.ReadBinaryInt(4); 118 | long messageSize = ef.ReadBinaryInt(4); 119 | buffer.Open(1024, FileClass::WRITE); 120 | if (messageSize > 0) { 121 | counter = 0; 122 | xorText = ""; 123 | for (int x = 0; x < messageSize; x++) { 124 | value = ef.ReadBinaryInt(1); 125 | if (counter == screenNameSize) { 126 | counter = 0; 127 | } 128 | value2 = (screenName.SubString(counter,1)).LastChar(); 129 | value = value ^ value2; 130 | WriteBuffer(buffer, value); //This allows us to handle RTL languages 131 | counter++; 132 | } 133 | ReadBuffer(buffer, xorText); //This allows us to handle RTL languages 134 | buffer.Close(); 135 | 136 | CLog.Debug(direction?"Recv:"+messageTime.GetString() +":"+xorText:"Sent:"+messageTime.GetString() +":"+xorText); 137 | rec = new LogRecordClass(recs, e.Name()); 138 | rec.SetCreated(messageTime); 139 | rec.SetComment(direction?"Recv:"+xorText:"Sent:"+xorText); 140 | Console.WriteLine(direction?"Recv: "+messageTime.GetString() +" " +xorText:"Sent: "+messageTime.GetString() +" " + xorText); 141 | LocalFileClass file2(); 142 | file2.Open(output + "\\" + screenName + "--" + e.Parent().Name() + "\\" + e.Name(), FileClass::APPEND); 143 | file2.SetCodePage(CodePageClass::UTF8); 144 | file2.WriteLine(direction?"Recv: "+messageTime.GetString() +" " +xorText+"\r\n":"Sent: "+messageTime.GetString() +" " + xorText+"\r\n"); 145 | file2.Close(); 146 | } 147 | ef.Skip(4); // Move forward beyond the footer to the next record 148 | } 149 | ef.Close(); 150 | parentFolder.AddDatamark(folderName, recs); 151 | } 152 | else { 153 | CLog.Fatal("Cannot open the file: " + e.FullPath()); 154 | } 155 | 156 | } 157 | } 158 | // Script specific code ends 159 | // 160 | 161 | // Case closedown code 162 | now.Now(); 163 | CLog.Info("Script Completed in " + (now.GetUnix() - start) + " seconds"); 164 | } 165 | 166 | 167 | String parseParentFolder(String path) { 168 | path = path.GetFilePath(); 169 | path = path.GetFilename(); 170 | return path; 171 | } 172 | 173 | class FilterDialogClass: DialogClass { 174 | StringEditClass variable; 175 | 176 | FilterDialogClass(DialogClass parent, MainClass v): 177 | DialogClass(parent, "Edit Conditions"), 178 | variable(this, "Enter the case sensitive screen name of the LOCAL user of Yahoo Chat Messenger", START, NEXT, 200, DEFAULT, 0, v.variable, 512, 0) 179 | { 180 | } 181 | } 182 | MainClass() { 183 | String usageText = "This script will parse Yahoo Messenger Chat Logs and place the output under Bookmarks->Log Records\n" 184 | "This script will work against selected files only.\n\n" 185 | "WARNING: There is no verifiable format to a Yahoo Messenger Chat log - therefore the script may hang if you attempt to parse Deleted chat logs.\n" 186 | "This script will parse deleted logs, but your mileage may vary. If the script does not stop, then cancel the execution of the script\n" 187 | "and select only a few logs at a time.\n\n" 188 | "Are you ready to proceed, or do you need to cancel to set up the script properly?\n"; 189 | int mbResponse = SystemClass::Message(SystemClass::MBOKCANCEL, "Template Script",usageText); 190 | if (mbResponse == SystemClass::CANCEL) { 191 | return; 192 | } 193 | 194 | FilterDialogClass dialog(null, this); 195 | if (dialog.Execute() != SystemClass::OK) 196 | SystemClass::Exit(); 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /diffs/strings_diff.diff: -------------------------------------------------------------------------------- 1 | --- binutils-2.20.51/binutils/strings.c 2009-11-10 23:54:32.000000000 -0500 2 | +++ strings.c 2014-07-21 15:27:42.000000000 -0400 3 | @@ -60,6 +60,10 @@ 4 | Written by Richard Stallman 5 | and David MacKenzie . */ 6 | 7 | +/* Changes made to get Unicode and ASCII strings in one run: 11/05/11 8 | + Jamie Levy 9 | +*/ 10 | + 11 | #include "sysdep.h" 12 | #include "bfd.h" 13 | #include "getopt.h" 14 | @@ -68,10 +72,15 @@ 15 | #include 16 | #include "bucomm.h" 17 | 18 | +/* 19 | + * it may not be a good idea to avoid some of these ranges in UTF-8, but it seems to keep out a lot of junk 20 | + * and also seems they are really invalid from the description of UTF-8 21 | + */ 22 | #define STRING_ISGRAPHIC(c) \ 23 | ( (c) >= 0 \ 24 | && (c) <= 255 \ 25 | - && ((c) == '\t' || ISPRINT (c) || (encoding == 'S' && (c) > 127))) 26 | + && ((c) == '\t' || ISPRINT (c) || (encoding == 'S' && (c) > 127 && (c) < 245 && (c) != 192 && (c) != 193))) 27 | + 28 | 29 | #ifndef errno 30 | extern int errno; 31 | @@ -102,8 +111,9 @@ 32 | static char *target; 33 | 34 | /* The character encoding format. */ 35 | -static char encoding; 36 | +char encoding; 37 | static int encoding_bytes; 38 | +char endian; //added this in case we ever really need to differentiate between endianness 39 | 40 | static struct option long_options[] = 41 | { 42 | @@ -161,7 +171,8 @@ 43 | print_filenames = FALSE; 44 | datasection_only = TRUE; 45 | target = NULL; 46 | - encoding = 's'; 47 | + encoding = 's'; //this doesn't matter, since we switch encodings.... 48 | + endian = 'l'; //by default we like little-endian 49 | 50 | while ((optc = getopt_long (argc, argv, "afhHn:ot:e:T:Vv0123456789", 51 | long_options, (int *) 0)) != EOF) 52 | @@ -221,7 +232,8 @@ 53 | case 'e': 54 | if (optarg[1] != '\0') 55 | usage (stderr, 1); 56 | - encoding = optarg[0]; 57 | + //encoding = optarg[0]; 58 | + endian = optarg[0]; 59 | break; 60 | 61 | case 'V': 62 | @@ -534,31 +546,71 @@ 63 | { 64 | char *buf = (char *) xmalloc (sizeof (char) * (string_min + 1)); 65 | 66 | + char encodings[] = {/*'L',*/ 'l', 's'}; // , 'S'}; //we actually don't need both 's' and 'S' 67 | + int ebytes[] = {/*4,*/ 2, 1 }; //, 1}; 68 | + 69 | + if (endian == 'b') { 70 | + //encodings[0] = 'B'; 71 | + //encodings[1] = 'b'; 72 | + encodings[0] = 'b'; 73 | + } 74 | + 75 | + fpos_t fpos; 76 | + int size; 77 | + size = sizeof(encodings); 78 | + 79 | while (1) 80 | { 81 | file_ptr start; 82 | - int i; 83 | + int i,n; 84 | long c; 85 | + c = 0; 86 | 87 | /* See if the next `string_min' chars are all graphic chars. */ 88 | tryline: 89 | - if (stop_point && address >= stop_point) 90 | - break; 91 | - start = address; 92 | + if (stop_point && address >= stop_point){ 93 | + break; 94 | + } 95 | + start = ftell(stream); //not sure we really need ftell, should just be able to use address... 96 | + fgetpos(stream, &fpos); 97 | + for (n = 0; n < size; n++){ 98 | + encoding = encodings[n]; 99 | + encoding_bytes = ebytes[n]; 100 | for (i = 0; i < string_min; i++) 101 | { 102 | c = get_char (stream, &address, &magiccount, &magic); 103 | - if (c == EOF) 104 | + if (c == EOF) 105 | return; 106 | - if (! STRING_ISGRAPHIC (c)) 107 | - /* Found a non-graphic. Try again starting with next char. */ 108 | - goto tryline; 109 | - buf[i] = c; 110 | + 111 | + if (! STRING_ISGRAPHIC (c) && n < size-1) { 112 | + /* Found a non-graphic. Try again starting with next encoding. */ 113 | + fsetpos(stream, &fpos); 114 | + address = start; 115 | + i = string_min; 116 | + } 117 | + else if (! STRING_ISGRAPHIC (c) ) { 118 | + /* counldn't find a graphic, move to next byte char */ 119 | + i = string_min; 120 | + } 121 | + else 122 | + buf[i] = c; 123 | + 124 | + } 125 | + if (STRING_ISGRAPHIC (c)) 126 | + /* we found a string of chars, break out */ 127 | + goto outside1; 128 | } 129 | + if (! STRING_ISGRAPHIC (c)) { 130 | + fsetpos(stream, &fpos); //may not need to reset back one byte, but i'm cautious 131 | + address = start; 132 | + c = get_char (stream, &address, &magiccount, &magic); 133 | + goto tryline; 134 | + } 135 | 136 | /* We found a run of `string_min' graphic characters. Print up 137 | to the next non-graphic character. */ 138 | 139 | + outside1: 140 | if (print_filenames) 141 | printf ("%s: ", filename); 142 | if (print_addresses) 143 | @@ -626,20 +678,23 @@ 144 | buf[i] = '\0'; 145 | fputs (buf, stdout); 146 | 147 | + /* may want to rewind by one char at the end of this...haven't yet decided */ 148 | while (1) 149 | { 150 | c = get_char (stream, &address, &magiccount, &magic); 151 | - if (c == EOF) 152 | + if (c == EOF) { 153 | break; 154 | - if (! STRING_ISGRAPHIC (c)) 155 | + } 156 | + if (! STRING_ISGRAPHIC (c)) { 157 | break; 158 | + } 159 | putchar (c); 160 | } 161 | 162 | putchar ('\n'); 163 | } 164 | } 165 | - 166 | + 167 | static void 168 | usage (FILE *stream, int status) 169 | { 170 | @@ -653,8 +708,9 @@ 171 | -t --radix={o,d,x} Print the location of the string in base 8, 10 or 16\n\ 172 | -o An alias for --radix=o\n\ 173 | -T --target= Specify the binary file format\n\ 174 | - -e --encoding={s,S,b,l,B,L} Select character size and endianness:\n\ 175 | - s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit\n\ 176 | + -e --encoding={b,l} Select endianness (all char sizes are done by default):\n\ 177 | + b = big-endian, l = little-endian the following are defunct:\n\ 178 | + s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit\n\ 179 | @ Read options from \n\ 180 | -h --help Display this information\n\ 181 | -v -V --version Print the program's version number\n")); 182 | -------------------------------------------------------------------------------- /fresponse/fresponse.bash: -------------------------------------------------------------------------------- 1 | # Credentials for the remote machine 2 | # so you can install fresponse remotely 3 | REMOTEUSER=NONE 4 | REMOTEPASS=NONE 5 | 6 | # IP addresses of remote machines: 7 | # example IPs below, replace with your own: 8 | IPs=(192.168.1.143 192.168.1.134) 9 | 10 | # F-Response Enterprise agent and INI file 11 | # You can change the name of the executable 12 | # but the INI file should be renamed accordingly too 13 | # These files should be in the same directory as this script 14 | EXE=f-response-ent.exe 15 | INI=f-response-ent.exe.ini 16 | # F-Reponse port: 17 | PORT=3260 18 | # F-Response username and password: 19 | FRUSER=NONE 20 | FRPASS=NONE 21 | # Name of service on remote machine: 22 | SERV=fresponse 23 | # Location of agent and INI file on remote machine: 24 | WIN=WINDOWS 25 | # All disks, memory, volumes that are mounted: 26 | # This is populated as nodes are discovered 27 | # (do not populate yourself) 28 | RESOURCES=() 29 | 30 | ####################################################### 31 | ## Author: Jamie Levy (Gleeda) 32 | ## 33 | ## fresponse.bash: F-Response Enterprise Helper Script 34 | ## 35 | ## Allows one to install and start the F-Response 36 | ## service from a Linux machine 37 | ## Also automates the discovery and logging into 38 | ## F-Response nodes 39 | ## This is a quick and dirty script and should 40 | ## be expanded as needed... 41 | ## Very minimal error checking 42 | ## 43 | ## Usage: ./fresponse.bash 44 | ## -I [IP addresses, comma delimited] 45 | ## -P [Port for F-Response. Default: 3260] 46 | ## -S [Service name for F-Reponse. Default: fresponse] 47 | ## -L [Location to install agent. Default: C:\WINDOWS] 48 | ## -E [Path to F-Response executable. Default: ./f-response-ent.exe] 49 | ## -N [Path to F-Response INI. Default: ./f-response-ent.exe.ini] 50 | ## 51 | ####################################################### 52 | 53 | bad=-65 54 | usage="Usage: $0 \n\t-I [IP addresses, comma delimited]\n\t-P [Port for F-Response. Default: $PORT]" 55 | usage="$usage\n\t-S [Service name for F-Reponse. Default: $SERV]\n\t-L [Location to install agent. Default: C:\\$WIN]" 56 | usage="$usage\n\t-E [Path to F-Response executable. Default: ./$EXE]\n\t-N [Path to F-Response INI. Default: ./$INI]\n" 57 | 58 | if [ $# -gt 0 ] #check for arguments 59 | then 60 | while getopts "I:P:S:L:h" OPTION 61 | do 62 | case $OPTION in 63 | P) 64 | PORT=$OPTARG 65 | ;; 66 | S) 67 | SERV=$OPTARG 68 | ;; 69 | I) 70 | IPs=(`echo $OPTARG |sed 's/,/ /g'`) 71 | ;; 72 | L) 73 | WIN=$OPTARG 74 | ;; 75 | E) 76 | EXE=$OPTARG 77 | ;; 78 | N) 79 | INI=$OPTARG 80 | ;; 81 | h) 82 | echo -e $usage 83 | exit 0 84 | ;; 85 | *) 86 | # unknown option 87 | echo "incorrect option $OPTION" 88 | echo 89 | echo -e $usage 90 | echo 91 | exit $bad 92 | ;; 93 | esac 94 | done 95 | 96 | fi 97 | 98 | if [[ $EUID -ne 0 ]]; 99 | then 100 | echo "This script must be run as root" 1>&2 101 | exit $bad 102 | fi 103 | 104 | 105 | if [[ ! -f $EXE ]]; 106 | then 107 | echo "File $EXE does not exist." 108 | echo "Please copy $EXE into this directory." 109 | exit $bad 110 | fi 111 | 112 | if [[ ! -f $INI ]]; 113 | then 114 | echo "File $INI does not exist." 115 | echo "Please copy $INI into this directory." 116 | exit $bad 117 | fi 118 | 119 | if [[ "$REMOTEUSER" == "NONE" ]]; 120 | then 121 | echo -n "[?] Enter remote machine user name (to install agent) :> " 122 | read REMOTEUSER 123 | fi 124 | 125 | if [[ "$REMOTEPASS" == "NONE" ]]; 126 | then 127 | echo -n "[?] Enter the remote machine password :> " 128 | read -s REMOTEPASS 129 | echo 130 | fi 131 | 132 | if [[ "$FRUSER" == "NONE" ]]; 133 | then 134 | echo -n "[?] Enter the F-Response user name :> " 135 | read FRUSER 136 | fi 137 | 138 | if [[ "$FRPASS" == "NONE" ]]; 139 | then 140 | echo -n "[?] Enter the F-Response password :> " 141 | read -s FRPASS 142 | echo 143 | fi 144 | 145 | function installagent { 146 | IP=$1 147 | echo "[*] Installing the F-Response driver and starting its service on $IP" 148 | mkdir -p /mnt/forensics 149 | if grep -qs "/mnt/forensics" /proc/mounts ; then 150 | umount /mnt/forensics 151 | fi 152 | mount -t cifs -o user="$REMOTEUSER%$REMOTEPASS",iocharset=utf8,file_mode=0777,dir_mode=0777 //$IP/c\$ /mnt/forensics 153 | 154 | if [[ $? -ne 0 ]]; 155 | then 156 | echo "[!!]" 157 | echo "[!!] Unable to connect to $IP, please check credentials!" 158 | echo "[!!]" 159 | IPs=( "${IPs[@]/$IP}" ) 160 | return 1 161 | fi 162 | 163 | cp $EXE /mnt/forensics/$WIN 164 | cp $INI /mnt/forensics/$WIN 165 | 166 | net rpc service create $SERV $SERV "%windir%\\$EXE" -I $IP -U "$REMOTEUSER%$REMOTEPASS" 167 | net rpc service start $SERV -I $IP -U "$REMOTEUSER%$REMOTEPASS" 168 | 169 | umount /mnt/forensics 170 | 171 | sleep 3 172 | return 0 173 | } 174 | 175 | 176 | function loginiscsi { 177 | IP=$1 178 | echo 179 | echo 180 | echo "[*] Finding ISCSI targets for $IP" 181 | 182 | iscsiadm --mode discoverydb --type sendtargets --portal $IP --op new 183 | iscsiadm --mode discoverydb --type sendtargets --portal $IP --op update --name discovery.sendtargets.auth.username --value $FRUSER 184 | iscsiadm --mode discoverydb --type sendtargets --portal $IP --op update --name discovery.sendtargets.auth.password --value $FRPASS 185 | MEM=`iscsiadm --mode discoverydb --type sendtargets --portal $IP --discover|grep pmem$ |awk '{print $2}'|cut -d\: -f1` 186 | iscsiadm --mode discoverydb --type sendtargets --portal $IP --discover 187 | iscsiadm --mode node --portal $IP --op update --name node.session.auth.username --value $FRUSER 188 | iscsiadm --mode node --portal $IP --op update --name node.session.auth.password --value $FRPASS 189 | 190 | echo 191 | echo 192 | echo "[*] Logging into $MEM\:pmem , $MEM\:disk-0 and $MEM\:vol-c" 193 | 194 | iscsiadm --mode node --targetname $MEM\:pmem --portal $IP --login 195 | iscsiadm --mode node --targetname $MEM\:disk-0 --portal $IP --login 196 | iscsiadm --mode node --targetname $MEM\:vol-c --portal $IP --login 197 | 198 | MOUNTDISK=/dev/disk/by-path/ip-$IP:$PORT-iscsi-$MEM\:disk-0-lun-0 199 | MOUNTMEM=/dev/disk/by-path/ip-$IP:$PORT-iscsi-$MEM\:pmem-lun-0 200 | MOUNTVOL=/dev/disk/by-path/ip-$IP:$PORT-iscsi-$MEM\:vol-c-lun-0 201 | 202 | RESOURCES+=($MEM\:pmem+$IP) 203 | RESOURCES+=($MEM\:disk-0+$IP) 204 | RESOURCES+=($MEM\:vol-c+$IP) 205 | 206 | sleep 3 207 | 208 | echo 209 | echo "The following devices are available:" 210 | echo 211 | echo MEM: 212 | readlink -f $MOUNTMEM 213 | echo 214 | echo "[*] To use Volatility (example):" 215 | echo " $ sudo python vol.py -f `readlink -f $MOUNTMEM` --profile=PROFILE PLUGIN" 216 | 217 | echo 218 | echo RAW DISK: 219 | readlink -f $MOUNTDISK 220 | echo 221 | echo "[*] To use sleuthkit (example): " 222 | echo " $ sudo mmls `readlink -f $MOUNTDISK`" 223 | echo "[*] For more, see http://wiki.sleuthkit.org/index.php?title=FS_Analysis" 224 | 225 | echo 226 | echo VOLUME: 227 | readlink -f $MOUNTVOL 228 | echo 229 | echo "[*] To mount the volume (example):" 230 | echo " $ sudo mount -t ntfs -o ro,show_sys_files,hide_hid_files `readlink -f $MOUNTVOL` /mnt/disk/" 231 | echo 232 | echo 233 | echo 234 | } 235 | 236 | function waitfordisconnect { 237 | echo "[*] Please use another shell for analysis." 238 | echo " Note: you must be root in order to access the devices." 239 | echo 240 | echo "[*] Press [ENTER] to disconnect....." 241 | echo " Note: this will close all devices." 242 | read any 243 | } 244 | 245 | function logoutiscsi { 246 | IP=$1 247 | echo "[*] Logging out and removing files for $IP...." 248 | 249 | for i in ${RESOURCES[@]} 250 | do 251 | item=$( echo $i |cut -d+ -f1 ) 252 | IP1=$( echo $i |cut -d+ -f2 ) 253 | if [ "$IP" == "$IP1" ]; then 254 | echo "[*] iscsiadm --mode node --targetname $item --portal $IP1 --logout" 255 | iscsiadm --mode node --targetname $item --portal $IP1 --logout 256 | RESOURCES=( "${RESOURCES[@]/$i}" ) 257 | fi 258 | done 259 | echo 260 | echo "[*] iscsiadm --mode discoverydb --type sendtargets --portal $IP --op delete" 261 | echo 262 | iscsiadm --mode discoverydb --type sendtargets --portal $IP --op delete 263 | } 264 | 265 | function removeagent { 266 | IP=$1 267 | echo "[*] Stopping F-Response service and deleting its files on $IP" 268 | 269 | net rpc service stop $SERV -I $IP -U "$REMOTEUSER%$REMOTEPASS" 270 | net rpc service delete $SERV -I $IP -U "$REMOTEUSER%$REMOTEPASS" 271 | mkdir -p /mnt/forensics 272 | mount -t cifs -o user="$REMOTEUSER%$REMOTEPASS",iocharset=utf8,file_mode=0777,dir_mode=0777 //$IP/c\$ /mnt/forensics 273 | 274 | sleep 2 275 | 276 | rm /mnt/forensics/$WIN/$EXE 277 | rm /mnt/forensics/$WIN/$INI 278 | 279 | umount /mnt/forensics 280 | } 281 | 282 | 283 | for IP in ${IPs[@]} 284 | do 285 | installagent $IP 286 | done 287 | 288 | for IP in ${IPs[@]} 289 | do 290 | loginiscsi $IP 291 | done 292 | 293 | waitfordisconnect 294 | 295 | for IP in ${IPs[@]} 296 | do 297 | logoutiscsi $IP 298 | removeagent $IP 299 | done 300 | -------------------------------------------------------------------------------- /get_plugins_2.0.bsh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Author: Jamie Levy (gleeda) 4 | # 5 | # get_plugins_2.0.bsh 6 | # 7 | # Downloads and installs Volatility 2.0 dependencies and plugins 8 | # 9 | # Updated for Volatility 2.0 10 | # 11 | # Tested on Ubuntu and Mac OSX 12 | # At the moment there is an issue with the distorm3 dependency on Mac OSX 13 | # 14 | 15 | 16 | #check we have svn installed 17 | k=`uname -a` 18 | svn=`which svn` 19 | if [[ $svn =~ svn ]] 20 | then 21 | echo svn already installed 22 | else 23 | if [[ "$k" =~ buntu ]] 24 | then 25 | apt-get install subversion -y 26 | elif [[ "$k" =~ Darwin ]] 27 | then 28 | port install subversion 29 | else 30 | echo please install subversion first! 31 | exit 0 32 | fi 33 | fi 34 | 35 | 36 | #download Volatility from the SVN repository 37 | 38 | echo "starting download from Volatility SVN repository..." 39 | start=`pwd`/Volatility 40 | svn checkout http://volatility.googlecode.com/svn/trunk Volatility 41 | 42 | #create a temporary directory 43 | #and download the helper libraries 44 | 45 | echo 46 | echo "installing some dependencies" 47 | 48 | if [[ "$k" =~ buntu ]] 49 | then 50 | apt-get install pcregrep libpcre++-dev python-dev -y 51 | elif [[ "$k" =~ Darwin ]] 52 | then 53 | port install pcre pcre++ 54 | fi 55 | 56 | pid=$$ 57 | mkdir temp_$pid 58 | cd temp_$pid 59 | 60 | echo 61 | echo "downloading and installing pycrypto" 62 | wget http://gitweb.pycrypto.org/\?p=crypto/pycrypto-2.0.x.git\;a=snapshot\;h=9e9641d0a9b88f09683b5f26d3b99c4a2e148da5\;sf=tgz -O pycrypto.tgz 63 | 64 | tar -xzvf pycrypto.tgz 65 | cd `tar -tf pycrypto.tgz | head -n1` 66 | python setup.py build 67 | python setup.py build install 68 | cd .. 69 | 70 | echo 71 | echo "downloading and installing distorm3" 72 | wget http://distorm.googlecode.com/files/distorm3-1.0.zip 73 | unzip distorm3-1.0.zip 74 | cd distorm3-1.0 75 | python setup.py build 76 | python setup.py build install 77 | cd .. 78 | 79 | echo 80 | echo "downloading and installing yara" 81 | wget http://yara-project.googlecode.com/files/yara-1.4.tar.gz 82 | wget http://yara-project.googlecode.com/files/yara-python-1.4a.tar.gz 83 | tar -xvzf yara-1.4.tar.gz 84 | cd yara-1.4 85 | if [[ "$k" =~ Darwin ]] 86 | then 87 | cat libyara/yara.h |sed 's/#include /#include "\/opt\/local\/include\/pcre.h"/' > tmp 88 | mv libyara/yara.h libyara/yara.h.bak 89 | mv tmp libyara/yara.h 90 | cat libyara/scan.c |sed 's/#include /#include "\/opt\/local\/include\/pcre.h"/' > tmp 91 | mv libyara/scan.c libyara/scan.c.bak 92 | mv tmp libyara/scan.c 93 | fi 94 | ./configure 95 | make 96 | make install 97 | cd .. 98 | tar -xvzf yara-python-1.4a.tar.gz 99 | cd yara-python-1.4a 100 | python setup.py build 101 | python setup.py build install 102 | 103 | if [[ "$k" =~ buntu ]] 104 | then 105 | if `cat /etc/ld.so.conf | grep "/usr/local/lib" 1>/dev/null 2>&1` 106 | then 107 | echo "yara configured" 108 | else 109 | echo "/usr/local/lib" >> /etc/ld.so.conf 110 | ldconfig 111 | echo "yara configured" 112 | fi 113 | fi 114 | 115 | cd ../.. 116 | rm -Rf temp_$pid 117 | 118 | cd $start 119 | wget http://jls-scripts.googlecode.com/files/timeliner_9-2011.zip 120 | unzip timeliner_9-2011.zip 121 | 122 | cd volatility/plugins 123 | wget http://malwarecookbook.googlecode.com/svn/trunk/malware.py 124 | wget http://malwarecookbook.googlecode.com/svn/trunk/zeusscan/zeusscan1.py 125 | wget http://malwarecookbook.googlecode.com/svn/trunk/zeusscan/zeusscan2.py 126 | wget http://malwarecookbook.googlecode.com/svn/trunk/zeroaccess/zeroaccess.py 127 | 128 | echo 129 | echo 130 | echo "Done. Change to your Volatility directory and type 'python vol.py -h'" 131 | echo "All plugins should populate under 'supported plugins'" 132 | 133 | if [[ "$k" =~ Darwin ]] 134 | then 135 | echo "You may have to manually install distorm3" 136 | fi 137 | echo 138 | 139 | -------------------------------------------------------------------------------- /misc_other/getregs.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | # 3 | # Author: Gleeda 4 | # 5 | # Grabs information from registry files in a memory image. In addition to Volatility, 6 | # you must have VolRip installed: 7 | # http://moyix.blogspot.com/2009/03/regripper-and-volatility-prototype.html 8 | # as well as VolReg: 9 | # http://www.cc.gatech.edu/~brendan/volatility/ 10 | # which requires Inline::Python: 11 | # http://search.cpan.org/~nine/Inline-Python/Python.pod 12 | # and Inline::C 13 | # http://search.cpan.org/~sisyphus/Inline-0.46/C/C.pod 14 | # 15 | # USAGE: 16 | # 17 | # getregs.pl 18 | # (this must be run in your Volatility directory) 19 | # 20 | # -f 21 | # -i 22 | # -rsys - RR system 23 | # -ruser - RR ntuser 24 | # -rsoft - RR software 25 | # -rsec - RR security 26 | # -rsam - RR samparse 27 | # -a - Autoruns keys 28 | # 29 | # 30 | # Redirect output into a text file 31 | # 32 | # *nix only 33 | # 34 | # Notes: currently has pauses in between each key that is queried for Autoruns (1s) and 35 | # registry file that RR is run against (3s) to allow bailouts with SIGINT (CTRL+C) 36 | # 37 | # depending on what plugins you have installed and system setup, you may see complaints about yara 38 | # pydasm, pefile or whatever not being available or in your path. just ignore these complaints 39 | # 40 | 41 | use Getopt::Long; 42 | 43 | $usage = "usage: $0\n(this must be run in your Volatility directory)\n\n\t-f \n\t-i \n"; 44 | $usage .= "\t-rsys - RR system\n\t-ruser - RR ntuser\n\t-rsoft - RR software\n\t-rsec - RR security\n\t-rsam - RR samparse\n\t-a - Autoruns keys\n\n"; 45 | 46 | &GetOptions("f=s", \$IMAGE, "i=s", \$INPUT, "rsys", \$SYSTEM, "rsoft", \$SOFT, "ruser", \$NTUSER, "rsec", \$SEC, "rsam", \$SAM, "a", \$AUTO); 47 | 48 | if (!defined $INPUT) { 49 | die $usage; 50 | } 51 | 52 | if (!defined $IMAGE) { 53 | die $usage; 54 | } 55 | 56 | #in case we wanna bail 57 | $SIG{INT} = \&my_init_handler; 58 | 59 | if (-e $INPUT) { 60 | open(FILE, $INPUT); 61 | }else{ 62 | die "Input file $INPUT not found!\n\n$usage"; 63 | } 64 | 65 | unless (-e $IMAGE) { 66 | die "Image file $IMAGE not found!\n\n$usage"; 67 | } 68 | 69 | if (!defined $SYSTEM && !defined $NTUSER && !defined $SOFT 70 | && !defined $SOFT && !defined $SEC && !defined $SAM && !defined $AUTO) { 71 | print "You must pick a mode of query\n"; 72 | die $usage; 73 | } 74 | 75 | #Get address and registry info from hivelist input file: 76 | while () { 77 | chomp; 78 | @line = split(" ", $_); 79 | if ($line[0] ne "Address") { 80 | push (@mylocs, $line[1]); 81 | push (@myadds, $line[0]); 82 | } 83 | } 84 | 85 | if (defined $SYSTEM) { 86 | &RR("system"); 87 | } 88 | 89 | if (defined $NTUSER) { 90 | &RR("ntuser"); 91 | } 92 | 93 | if (defined $SOFT) { 94 | &RR("software"); 95 | } 96 | 97 | if (defined $SEC) { 98 | &RR("security"); 99 | } 100 | 101 | if (defined $SAM) { 102 | &RR("sam"); 103 | } 104 | 105 | if (defined $AUTO) { 106 | &autoruns; 107 | } 108 | 109 | sub autoruns() { 110 | for $i (0..$#myadds) { 111 | print "*"x100 . "\n"; 112 | print "* FILE: $mylocs[$i] ($myadds[$i])\n"; 113 | print "* Value (if any):\n\n"; 114 | system ("python ./volatility printkey -f $IMAGE -o $myadds[$i] -r false"); 115 | print "*"x100 . "\n\n\n"; 116 | } 117 | sleep 1; 118 | } 119 | 120 | sub RR() { 121 | my $plugins = shift; 122 | for $i (0..$#myadds) { 123 | print "*"x100 . "\n"; 124 | print "* FILE: $mylocs[$i] ($myadds[$i])\n*\n"; 125 | system("perl ./rip.pl -r $IMAGE\@$myadds[$i] -f $plugins"); 126 | print "*"x100 . "\n\n\n"; 127 | sleep 3; 128 | } 129 | } 130 | 131 | 132 | sub my_init_handler{ 133 | die "Exiting....\n\n"; 134 | } 135 | -------------------------------------------------------------------------------- /misc_other/mac.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | foreach $filename (@ARGV) { 4 | ($dev, $inode, $mode, $nlink, $uid, $gid, $rdev, 5 | $size, $atime, $mtime, $ctime, $blksize, $blocks) = lstat($filename); 6 | print "$filename (MAC): $mtime,$atime,$ctime\n"; 7 | print "$filename (MAC): " . localtime($mtime) . "," . localtime($atime) . "," . localtime($ctime) . "\n"; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /misc_python/collect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Author: Gleeda 3 | # 4 | # collect.py 5 | # 6 | #This program is free software; you can redistribute it and/or 7 | #modify it under the terms of the GNU General Public License 8 | #as published by the Free Software Foundation; either version 9 | #2 of the License, or (at your option) any later version. 10 | # 11 | 12 | import shutil, errno 13 | import getopt, sys, os 14 | 15 | allowable = [ 16 | ".dll", 17 | ".sys", 18 | ".exe", 19 | ".scr", 20 | ".drv", 21 | ] 22 | 23 | def usage(): 24 | print "collect.py:" 25 | print " - collects files specified in a text file) from one 'disk' to another" 26 | print "\t-h, --help : Print this help message" 27 | print "\t-s, --source : Source folder/mount point" 28 | print "\t-t, --target : Target folder for collected files (must be absolute and non-existent before running)" 29 | print "\t-f, --files : List of files to collect" 30 | print "\t-d, --driverloc : Location of pathless drivers to cut down on runtime\n" 31 | print "\t-a, --allfiles : Collect all files and not just those in allowable extensions (off by default)\n" 32 | print "Example usage:\n $ python collect.py -s /path/to/source -t /path/to/target -f /path/to/filelist.txt" 33 | 34 | def rreplace(s, old, new, occurrence): 35 | li = s.rsplit(old, occurrence) 36 | return new.join(li) 37 | 38 | def GetAvailable(dst, fname): 39 | ''' 40 | Since some modules are listed without full paths, we'll try to find 41 | and available file name. Hopefully there aren't any more than 20 42 | modules with the same name... nah... 43 | ''' 44 | if os.path.exists(os.path.join(dst, fname)): 45 | for i in range(1, 20): 46 | if not os.path.exists(os.path.join(dst, fname + "." + str(i))): 47 | return fname + "." + str(i) 48 | return fname + ".past_threshold" 49 | else: 50 | return fname 51 | 52 | def ProcessPathlessFiles(src, dst, files): 53 | subdirlist = [] 54 | os.chdir(src) 55 | for fname in os.listdir(src): 56 | if fname.lower() in files and os.path.isfile(os.path.join(src, fname)): 57 | thefile = os.path.join(src, fname) 58 | print "Copying " + thefile + " to " + dst 59 | dstfile = GetAvailable(dst, fname) 60 | shutil.copy2(thefile, os.path.join(dst, dstfile)) 61 | elif os.path.isdir(os.path.join(src, fname)): 62 | subdirlist.append(os.path.join(src, fname)) 63 | for subdir in subdirlist: 64 | ProcessPathlessFiles(subdir, dst, files) 65 | 66 | def GetSelectTypeFiles(src, dst): 67 | subdirlist = [] 68 | os.chdir(src) 69 | for fname in os.listdir(src): 70 | if os.path.isfile(os.path.join(src, fname)) and os.path.splitext(fname.lower())[1] in allowable: 71 | thefile = os.path.join(src, fname) 72 | print "Copying " + thefile + " to " + dst 73 | dstfile = GetAvailable(dst, fname) 74 | shutil.copy2(thefile, os.path.join(dst, dstfile)) 75 | elif os.path.isdir(os.path.join(src, fname)): 76 | subdirlist.append(os.path.join(src, fname)) 77 | for subdir in subdirlist: 78 | ProcessPathlessFiles(subdir, dst, files) 79 | 80 | def main(): 81 | try: 82 | opts, args = getopt.getopt(sys.argv[1:], "has:t:f:d:", ["help", "allfiles", "source=", "target=", "files=", "driverloc="]) 83 | except getopt.GetoptError, err: 84 | print str(err) 85 | sys.exit(2) 86 | 87 | source = None 88 | target = None 89 | files = None 90 | drivers = None 91 | all = False 92 | 93 | for o, a in opts: 94 | if o in ("-h", "--help"): 95 | usage() 96 | return 97 | elif o in ("-s", "--source"): 98 | source = a 99 | elif o in ("-t", "--target"): 100 | target = a 101 | elif o in ("-f", "--files"): 102 | files = a 103 | elif o in ("-d", "--driverloc"): 104 | drivers = a 105 | elif o in ("-a", "--allfiles"): 106 | all = True 107 | else: 108 | assert False, "unhandled option\n\n" 109 | return 110 | 111 | if source == None or target == None or files == None: 112 | print "You must specify source, target and file list!" 113 | usage() 114 | return 115 | 116 | f = open(files, "r") 117 | items = [] 118 | for line in f.readlines(): 119 | l = line.replace("\n", "") 120 | src = source + "/" + l 121 | print src, target + "/" + l 122 | if all and os.path.isdir(src): 123 | try: 124 | shutil.copytree(src, target + "/" + l) 125 | print "Copied " + src + " to " + target 126 | except OSError: 127 | print "Target directory must be empty!" 128 | return 129 | elif os.path.isdir(src): 130 | GetSelectTypeFiles(src, dst) 131 | elif os.path.isfile(src): 132 | thefile = l.split("/")[-1] 133 | thepath = target + "/" + rreplace(l, thefile, '', 1) 134 | try: 135 | os.makedirs(thepath) 136 | except OSError as exc: 137 | if exc.errno == errno.EEXIST: 138 | pass 139 | else: raise 140 | try: 141 | shutil.copy2(src, target + "/" + l) 142 | print "Copied file " + src + " to " + target 143 | except : #OSError: 144 | print "ERROR copying " + src + " to " + target + "/" + l 145 | return 146 | else: 147 | items.append(l) 148 | 149 | if len(items) > 0: 150 | if drivers != None: 151 | if os.path.isdir(drivers): 152 | source = drivers 153 | os.mkdir(target + "/drivers") 154 | ProcessPathlessFiles(source, target + "/drivers", items) 155 | 156 | if __name__ == "__main__": 157 | main() 158 | -------------------------------------------------------------------------------- /misc_python/create_cybox_demo.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: Jamie Levy (gleeda) 3 | email: jamie.levy@gmail.com 4 | 5 | Example script to show how to create CybOX observables 6 | You must install python-cybox before using: 7 | https://github.com/CybOXProject/python-cybox 8 | 9 | Many thanks to Ivan Kirillov and Greg Back for answering many questions and bug fixes :-) 10 | """ 11 | import sys 12 | import base64 13 | from cybox.core import Observables, Observable, Object, ObservableComposition 14 | from cybox import helper 15 | from cybox.objects.process_object import Process, ImageInfo 16 | from cybox.objects.mutex_object import Mutex 17 | from cybox.objects.file_object import File 18 | from cybox.objects.win_service_object import WinService 19 | from cybox.objects.win_registry_key_object import WinRegistryKey 20 | 21 | 22 | # this can be changed to an output file 23 | outfd = sys.stdout 24 | 25 | # create an Observable object: 26 | observables_doc = Observables([]) 27 | 28 | # add some different observables: 29 | # you don't have to use every member and there are other members that are not being utilized here: 30 | observables_doc.add(Process.from_dict({"name": "Process.exe", 31 | "pid": 90, 32 | "parent_pid": 10, 33 | #"creation_time": "", 34 | "image_info": {"command_line": "Process.exe /c blah.txt"}})) 35 | 36 | observables_doc.add(File.from_dict({"file_name": "file.txt", 37 | "file_extension": "txt", 38 | "file_path": "path\\to\\file.txt"})) 39 | 40 | 41 | observables_doc.add(helper.create_ipv4_observable("192.168.1.101")) 42 | 43 | observables_doc.add(helper.create_url_observable("somedomain.com")) 44 | 45 | observables_doc.add(WinService.from_dict({"service_name": "Service Name", 46 | "display_name": "Service Display name", 47 | "startup_type": "Service type", 48 | "service_status": "Status", 49 | "service_dll": "Somedll.dll", 50 | "started_as": "Start", 51 | "group_name": "Group name", 52 | "startup_command_line": "Commandline"})) 53 | 54 | observables_doc.add(WinRegistryKey.from_dict({"hive": "SYSTEM", 55 | "key": "some\\registry\\key", 56 | "number_values": 2, 57 | "values": [{"name": "Something", 58 | "datatype": "REG_DWORD", #or whatever it is... 59 | "data": "Something else"}, 60 | {"name": "Another", 61 | "datatype": "REG_BINARY", #or whatever it is... 62 | "data": base64.b64encode("\x90\x90\x90")}], #binary stuff must be normalized, base64 is the usual 63 | "number_subkeys": 1, 64 | # subkeys have the same members as keys: 65 | "subkeys": [{"key": "SubkeyName", "number_values": 1, 66 | "values": [{"name": "SubkeyVal", "datatype": "REG_DWORD", "data": "Subkey val data"}]}] 67 | })) 68 | 69 | observables_doc.add(Mutex.from_dict({"name": "Some_MUTEX!!!"})) 70 | 71 | # we can also specify conditions: 72 | proc = Process.from_dict({"name": "anotherProcess.exe", 73 | "pid": 102, 74 | "parent_pid": 10, 75 | "image_info": {"command_line": "anotherProcess.exe /c blahblah.bat"}}) 76 | proc.name.condition = "Equals" 77 | proc.image_info.command_line.condition = "Contains" 78 | # we need the same object so we can use the id for the compositions below 79 | obs1 = Observable(proc) 80 | observables_doc.add(obs1) 81 | 82 | file = File.from_dict({"file_name": "blah", "file_extension": "bat"}) 83 | file.file_name.condition = "Contains" 84 | file.file_extension.condition = "Equals" 85 | obs2 = Observable(file) 86 | observables_doc.add(obs2) 87 | 88 | mutex = Mutex.from_dict({"name": "Some_OTHER_MUTEX!!!"}) 89 | obs3 = Observable(mutex) 90 | observables_doc.add(obs3) 91 | 92 | # to add logic: 93 | # normally you'd probably have logic for all items, but this is just a demo, not reality 94 | oproc_ref = Observable() 95 | oproc_ref.id_ = None 96 | oproc_ref.idref = obs1.id_ 97 | 98 | ofile_ref = Observable() 99 | ofile_ref.id_ = None 100 | ofile_ref.idref = obs2.id_ 101 | 102 | omutex_ref = Observable() 103 | omutex_ref.id_ = None 104 | omutex_ref.idref = obs3.id_ 105 | 106 | o_comp = Observable(ObservableComposition(operator = "OR")) 107 | o_comp.observable_composition.add(oproc_ref) 108 | o_comp.observable_composition.add(ofile_ref) 109 | 110 | o_comp2 = Observable(ObservableComposition(operator = "AND")) 111 | o_comp2.observable_composition.add(omutex_ref) 112 | 113 | o_comp.observable_composition.add(o_comp2) 114 | 115 | # add our composition to the observables: 116 | observables_doc.add(o_comp) 117 | 118 | # output to stdout or file or whatever: 119 | outfd.write(observables_doc.to_xml()) 120 | 121 | -------------------------------------------------------------------------------- /misc_python/getIPInfo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | import getopt, sys, os 4 | import dns.resolver 5 | 6 | try: 7 | from pygeoip import * 8 | except ImportError: 9 | print "You must install pygeoip:\n\t https://github.com/appliedsec/pygeoip\n" 10 | sys.exit(2) 11 | 12 | try: 13 | from openpyxl.workbook import Workbook 14 | from openpyxl.writer.excel import ExcelWriter 15 | from openpyxl.cell import get_column_letter 16 | from openpyxl.styles import Color, Fill, Style, PatternFill, Border, Side, Alignment, Protection, Font 17 | from openpyxl.cell import Cell 18 | from openpyxl import load_workbook 19 | has_openpyxl = True 20 | except ImportError: 21 | has_openpyxl = False 22 | 23 | 24 | ''' 25 | Author: Gleeda 26 | 27 | This program is free software; you can redistribute it and/or 28 | modify it under the terms of the GNU General Public License 29 | as published by the Free Software Foundation; either version 30 | 2 of the License, or (at your option) any later version. 31 | 32 | getIPInfo.py 33 | 34 | -f 35 | -e 36 | -g 37 | -c [optional: this will add color to excel file] 38 | 39 | What you need: 40 | 1) File with IP addresses, one per line 41 | 2) GeoLiteCity.dat: http://dev.maxmind.com/geoip/legacy/geolite/ 42 | 3) Pygeoip: https://github.com/appliedsec/pygeoip 43 | 4) OpenPyxl (optional, for excel output): https://pypi.python.org/pypi/openpyxl 44 | 5) Internet connection for lookups 45 | ''' 46 | 47 | robtex = "https://www.robtex.com/en/advisory/ip" 48 | 49 | codes = { 50 | "127.0.0.2": "Direct UBE sources, spam operations & spam services", 51 | "127.0.0.3": "Direct snowshoe spam sources detected via automation", 52 | "127.0.0.4": "CBL (3rd party exploits such as proxies, trojans, etc.)", 53 | "127.0.0.5": "CBL (3rd party exploits such as proxies, trojans, etc.)", 54 | "127.0.0.6": "CBL (3rd party exploits such as proxies, trojans, etc.)", 55 | "127.0.0.7": "CBL (3rd party exploits such as proxies, trojans, etc.)", 56 | "127.0.0.10": "End-user Non-MTA IP addresses set by ISP outbound mail policy", 57 | "127.0.0.11": "End-user Non-MTA IP addresses set by ISP outbound mail policy", 58 | } 59 | 60 | servers = [ 61 | "zen.spamhaus.org", 62 | "spam.abuse.ch", 63 | "virbl.dnsbl.bit.nl", 64 | "dnsbl.inps.de", 65 | "ix.dnsbl.manitu.net", 66 | "dnsbl.sorbs.net", 67 | "bl.spamcannibal.org", 68 | "bl.spamcop.net", 69 | "dnsbl-1.uceprotect.net", 70 | "dnsbl-2.uceprotect.net", 71 | "dnsbl-3.uceprotect.net", 72 | "db.wpbl.info", 73 | "zen.spamhaus.org", 74 | "dbl.spamhaus.org", 75 | "pbl.spamhaus.org", 76 | "xbl.spamhaus.org", 77 | "sbl.spamhaus.org", 78 | "cbl.abuseat.org", 79 | ] 80 | 81 | if has_openpyxl: 82 | BoldStyle = Style(font=Font(name='Calibri', 83 | size=11, 84 | bold=True, 85 | italic=False, 86 | vertAlign=None, 87 | underline='none', 88 | strike=False, 89 | color='FFFFFFFF'), 90 | fill=PatternFill(fill_type="solid", 91 | start_color='FF000000', 92 | end_color='FF000000')) 93 | 94 | RedStyle = Style(font=Font(name='Calibri', 95 | size=11, 96 | bold=False, 97 | italic=False, 98 | vertAlign=None, 99 | underline='none', 100 | strike=False, 101 | color='FF000000'), 102 | border=Border(left=Side(border_style="thick", 103 | color='FF000000'), 104 | right=Side(border_style="thick", 105 | color='FF000000'), 106 | top=Side(border_style="thick", 107 | color='FF000000'), 108 | bottom=Side(border_style="thick", 109 | color='FF000000'), 110 | diagonal=Side(border_style="thick", 111 | color='FF000000'), 112 | diagonal_direction=0, 113 | outline=Side(border_style="thick", 114 | color='FF000000'), 115 | vertical=Side(border_style="thick", 116 | color='FF000000'), 117 | horizontal=Side(border_style="thick", 118 | color='FF000000')), 119 | fill=PatternFill(start_color = 'FFFF0000', 120 | end_color = 'FFFF0000', 121 | fill_type = 'solid')) 122 | 123 | GreenStyle = Style(font=Font(name='Calibri', 124 | size=11, 125 | bold=False, 126 | italic=False, 127 | vertAlign=None, 128 | underline='none', 129 | strike=False, 130 | color='FF000000'), 131 | border=Border(left=Side(border_style="thin", 132 | color='FF000000'), 133 | right=Side(border_style="thin", 134 | color='FF000000'), 135 | top=Side(border_style="thin", 136 | color='FF000000'), 137 | bottom=Side(border_style="thin", 138 | color='FF000000'), 139 | diagonal=Side(border_style="thin", 140 | color='FF000000'), 141 | diagonal_direction=0, 142 | outline=Side(border_style="thin", 143 | color='FF000000'), 144 | vertical=Side(border_style="thin", 145 | color='FF000000'), 146 | horizontal=Side(border_style="thin", 147 | color='FF000000'))) 148 | #fill=PatternFill(start_color = "FF00FF00", 149 | # end_color = "FF00FF00", 150 | # fill_type = "solid")) 151 | 152 | def checkbl(ip): 153 | for server in servers: 154 | try: 155 | my_resolver = dns.resolver.Resolver() 156 | query = ip + '.' + server 157 | res = socket.gethostbyname(query) 158 | answer_txt = "{0}".format(my_resolver.query(query, "TXT")[0] or "") 159 | return ["Yes", codes[res], server, answer_txt] 160 | except socket.gaierror: 161 | pass 162 | return ["", "", "", ""] 163 | 164 | def usage(): 165 | print sys.argv[0], "\n" 166 | print " -f " 167 | print " -e " 168 | print " -g " 169 | print " -c [optional: this will add color to excel file]" 170 | 171 | def main(): 172 | ipfile = None 173 | excelfile = None 174 | GeoLite = None 175 | wb = None 176 | ws = None 177 | color = False 178 | header = ["IP Address", "Country Code", "Country Name", "Blacklisted", "Code", "Server", "Details", "Robtex Info"] 179 | try: 180 | opts, args = getopt.getopt(sys.argv[1:], "hf:g:e:c", ["help", "file=", "geolitecity=", "excelfile=", "coloring"]) 181 | except getopt.GetoptError, err: 182 | print str(err) 183 | sys.exit(2) 184 | 185 | for o, a in opts: 186 | if o in ("-h", "--help"): 187 | usage() 188 | sys.exit(0) 189 | elif o in ("-e", "--excelfile"): 190 | if not has_openpyxl: 191 | print "You must install OpenPyxl 2.1.2+ for xlsx format:\n\thttps://pypi.python.org/pypi/openpyxl" 192 | sys.exit(-1) 193 | excelfile = a 194 | wb = Workbook(optimized_write = True) 195 | ws = wb.create_sheet() 196 | ws.title = "IP Address Info" 197 | ws.append(header) 198 | elif o in ("-g", "--geolitecity"): 199 | GeoLite = a 200 | elif o in ("-f", "--file"): 201 | if os.path.isfile(a): 202 | ipfile = open(a, "r") 203 | else: 204 | print a + " is not a file" 205 | usage() 206 | sys.exit(-1) 207 | elif o in ("-c", "--coloring"): 208 | color = True 209 | else: 210 | assert False, "unhandled option\n\n" 211 | sys.exit(-2) 212 | 213 | if ipfile == None or GeoLite == None: 214 | usage() 215 | sys.exit(2) 216 | 217 | gi = GeoIP(GeoLite) 218 | if excelfile == None: 219 | print ",".join(x for x in header) 220 | 221 | total = 1 222 | for i in ipfile.readlines(): 223 | item = i.strip() 224 | rev = '.'.join(item.split('.')[::-1]) 225 | gistuff = gi.record_by_addr(item) 226 | ip = "/".join(item.split(".")) 227 | robtex_url = "{0}/{1}".format(robtex, ip) 228 | bl = checkbl(rev) 229 | line = [item, gistuff["country_code"], gistuff["country_name"], bl[0], bl[1], bl[2], bl[3], robtex_url] 230 | if excelfile == None: 231 | print ",".join(x for x in line) 232 | else: 233 | ws.append(line) 234 | total += 1 235 | 236 | if excelfile: 237 | wb.save(filename = excelfile) 238 | if not color: 239 | return 240 | wb = load_workbook(filename = excelfile) 241 | ws = wb.get_sheet_by_name(name = "IP Address Info") 242 | for col in xrange(1, len(header) + 1): 243 | ws.cell("{0}{1}".format(get_column_letter(col), 1)).style = BoldStyle 244 | 245 | for row in xrange(2, total + 1): 246 | for col in xrange(1, len(header) + 1): 247 | if ws.cell("{0}{1}".format(get_column_letter(4), row)).value == "Yes": 248 | ws.cell("{0}{1}".format(get_column_letter(col), row)).style = RedStyle 249 | else: 250 | ws.cell("{0}{1}".format(get_column_letter(col), row)).style = GreenStyle 251 | wb.save(filename = excelfile) 252 | 253 | if __name__ == "__main__": 254 | main() 255 | -------------------------------------------------------------------------------- /misc_python/getmalwaredomains.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | #Author: Gleeda 4 | # 5 | #This program is free software; you can redistribute it and/or 6 | #modify it under the terms of the GNU General Public License 7 | #as published by the Free Software Foundation; either version 8 | #2 of the License, or (at your option) any later version. 9 | # 10 | # getmalwaredomains.py 11 | # 12 | # collects domain/IP/Date/Reverse Lookup information 13 | # from malwaredomainlist.com 14 | import os, sys 15 | import getopt 16 | import httplib 17 | import sqlite3 18 | 19 | DBNAME = "malwaredomains.db" 20 | MAXWAIT = (60*10) # ten minutes 21 | 22 | def usage(): 23 | print 'getmalwaredomains.py:' 24 | print ' - collects domain/IP/Date/Reverse Lookup information from malwaredomainlist.com' 25 | print '\t-h, --help : print help message' 26 | print '\t-d, --database : sqlite3 db to store domain info (default malwaredomains.db)' 27 | print '\t-b, --bulk : bulk upload up to given number of pages' 28 | print '\t-u, --update : process entries from update link' 29 | print '\t-a, --add : add a particular domain\n' 30 | 31 | class malwaredomains: 32 | def adddomain(self, d): 33 | domain = d 34 | url = 'http://www.malwaredomainlist.com/mdl.php?search=' + domain + '&colsearch=All&quantity=50' 35 | try: 36 | conn = httplib.HTTPConnection('www.malwaredomainlist.com') 37 | conn.request('GET', '/mdl.php?search=' + domain + '&colsearch=All&quantity=50') 38 | response = conn.getresponse().read() 39 | if response.find('Date (UTC)') != -1: 40 | print "Page exists: %s" % url 41 | return response 42 | else: 43 | print "Unable to connect!" 44 | except Exception, e: 45 | print "Error connecting:", e 46 | pass 47 | 48 | return None 49 | 50 | def getupdates(self): 51 | url = 'http://www.malwaredomainlist.com/update.php' 52 | try: 53 | conn = httplib.HTTPConnection('www.malwaredomainlist.com') 54 | conn.request('GET', '/update.php') 55 | response = conn.getresponse().read() 56 | if response.find('Date (UTC)') != -1: 57 | print "Page exists: %s" % url 58 | return response 59 | else: 60 | print "Unable to connect!" 61 | except Exception, e: 62 | print "Error connecting:", e 63 | pass 64 | 65 | return None 66 | 67 | 68 | def getdomains(self, page): 69 | url = 'http://www.malwaredomainlist.com/mdl.php' 70 | page = page 71 | try: 72 | conn = httplib.HTTPConnection('www.malwaredomainlist.com') 73 | conn.request('GET', '/mdl.php?inactive=&sort=Date&search=&colsearch=All&ascordesc=DESC&quantity=100&page=' + page) 74 | response = conn.getresponse().read() 75 | if response.find('Date (UTC)') != -1: 76 | print "Page exists: %s" % url 77 | return response 78 | else: 79 | print "Unable to connect!" 80 | except Exception, e: 81 | print "Error connecting:", e 82 | pass 83 | 84 | return None 85 | 86 | class Domains: 87 | def __init__(self, data): 88 | self.data = data 89 | self.column_names = {0 : 'Date', 1 : 'Domain', 2 : 'IP', 3 : 'Rlookup', 4 : 'Description', 5 : 'Registrant', 6 : 'ASN', 7 : 'COUNTRY'} 90 | 91 | def process_entries(self, str): 92 | items = [] 93 | line = str 94 | line.rstrip() 95 | line.lstrip() 96 | line.strip() 97 | line = line.replace('', '') 98 | line = line.replace('', '') 99 | line = line.replace('', '') 100 | line = line.strip("") 101 | return line 102 | 103 | 104 | def process_column(self, column, ncol): 105 | start_value = column.find('>') 106 | if start_value == -1: 107 | return 108 | 109 | column = column[start_value+1:] 110 | 111 | end_column = column.find('') 112 | if end_column == -1: 113 | return 114 | 115 | str = column[0:end_column] 116 | if str.find('img src') != -1: 117 | return None 118 | if self.column_names[ncol] == 'Date': 119 | if str.find("/") != -1 and str.find(":") != -1: 120 | date = self.process_entries(str) 121 | date = date.replace("_", " ") 122 | return {'dates': date} 123 | else: 124 | return None 125 | elif self.column_names[ncol] == 'Domain': 126 | domain = self.process_entries(str) 127 | domain = domain.lstrip('.') 128 | return {'domains': domain} 129 | elif self.column_names[ncol] == 'Rlookup': 130 | rlookup = self.process_entries(str) 131 | rlookup = rlookup.rstrip('.') 132 | return {'rlookups': rlookup} 133 | elif self.column_names[ncol] == 'IP': 134 | ip = self.process_entries(str) 135 | return {'ips': ip} 136 | 137 | return None 138 | 139 | def process_row(self, row): 140 | end_row = row.find('') 141 | if end_row == -1: 142 | return 143 | 144 | row = row[0:end_row] 145 | offset = 0 146 | ncol = 0 147 | 148 | row_info = {} 149 | 150 | while row[offset:].find('') 169 | if end_table != -1: 170 | table_data = data[start_table:start_table+end_table] 171 | 172 | offset = 0 173 | nrow = 0 174 | domain_info = [] 175 | 176 | while table_data[offset:].find(' 0: 181 | row_info = self.process_row(row) 182 | domain_info.append(row_info) 183 | offset += ofs 184 | nrow += 1 185 | 186 | return domain_info 187 | 188 | def init(): 189 | global DBNAME 190 | if os.path.isfile(DBNAME): 191 | return 192 | 193 | conn = sqlite3.connect(DBNAME) 194 | curs = conn.cursor() 195 | 196 | curs.executescript(''' 197 | CREATE TABLE domains ( 198 | id INTEGER PRIMARY KEY, 199 | domain TEXT, 200 | ip TEXT, 201 | rlookup TEXT, 202 | date TEXT 203 | ); 204 | ''') 205 | curs.close() 206 | 207 | if os.path.isfile(DBNAME): 208 | print "Success." 209 | else: 210 | print "Failed." 211 | 212 | def process_domains(domain_info): 213 | conn = sqlite3.connect(DBNAME) 214 | conn.text_factory = str 215 | cur = conn.cursor() 216 | 217 | for info in domain_info: 218 | cur.execute("SELECT COUNT(*) FROM domains where domain=? AND ip=? AND rlookup=? AND date=?", 219 | (info['domains'], 220 | info['ips'], 221 | info['rlookups'], 222 | info['dates'])) 223 | count = cur.fetchone()[0] 224 | if count < 1: 225 | cur.execute("INSERT INTO domains VALUES(null, ?, ?, ?, ?)", (info['domains'], info['ips'], info['rlookups'], info['dates'])) 226 | print '[domain]', info['domains'], '\n\t[IP] ', info['ips'] 227 | else: 228 | print "NOT ADDED " +info['domains'] + '\n\twith IP: ' + info['ips'] + ' already in database, not added' 229 | 230 | conn.commit() 231 | conn.close() 232 | 233 | 234 | def main(): 235 | try: 236 | opts, args = getopt.getopt(sys.argv[1:], "hb:a:d:u", ["help", "bulk=", "add=", "database="]) 237 | except getopt.GetoptError, err: 238 | print str(err) 239 | sys.exit(2) 240 | 241 | bulk = False 242 | update = False 243 | domain = None 244 | global DBNAME 245 | 246 | for o, a in opts: 247 | if o in ("-h", "--help"): 248 | usage() 249 | sys.exit(2) 250 | elif o in ("-b", "--bulk"): 251 | bulk = True 252 | pages = a 253 | elif o in ("-a", "--add"): 254 | domain = a 255 | elif o in ("-d", "--database"): 256 | DBNAME = a 257 | elif o in ("-u", "--update"): 258 | update = True 259 | else: 260 | assert False, "unhandled option\n\n" 261 | sys.exit(2) 262 | 263 | if not bulk and domain == None and not update: 264 | print 'You must choose either bulk, update or single addition mode!\n' 265 | usage() 266 | sys.exit(-1) 267 | 268 | init() 269 | 270 | md = malwaredomains() 271 | data = None 272 | 273 | if domain != None: 274 | data = md.adddomain(domain) 275 | if data == None: 276 | print "Cannot find url!" 277 | else: 278 | fs = Domains(data) 279 | domain_info = fs.extract() 280 | process_domains(domain_info) 281 | if update: 282 | data = md.getupdates() 283 | if data == None: 284 | print "Cannot find url!" 285 | else: 286 | fs = Domains(data) 287 | domain_info = fs.extract() 288 | process_domains(domain_info) 289 | if bulk: 290 | if int(pages) >= 0: 291 | i = 0 292 | while i <= int(pages): 293 | data = md.getdomains(str(i)) 294 | 295 | if data == None: 296 | print "Cannot find url!" 297 | return 298 | else: 299 | fs = Domains(data) 300 | domain_info = fs.extract() 301 | 302 | process_domains(domain_info) 303 | i = i+1 304 | 305 | if __name__ == "__main__": 306 | main() 307 | 308 | 309 | -------------------------------------------------------------------------------- /misc_python/jobparser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import os 4 | import getopt 5 | import struct 6 | 7 | """ 8 | Author: Gleeda 9 | 10 | This program is free software; you can redistribute it and/or 11 | modify it under the terms of the GNU General Public License 12 | as published by the Free Software Foundation; either version 13 | 2 of the License, or (at your option) any later version. 14 | 15 | jobparser.py 16 | Parses job files created from `at` commands 17 | 18 | -f 19 | -d 20 | """ 21 | 22 | 23 | # http://msdn.microsoft.com/en-us/library/2d1fbbab-fe6c-4ae5-bdf5-41dc526b2439%28v=prot.13%29#id11 24 | products = { 25 | 0x400:"Windows NT 4.0", 26 | 0x500:"Windows 2000", 27 | 0x501:"Windows XP", 28 | 0x600:"Windows Vista", 29 | 0x601:"Windows 7", 30 | 0x602:"Windows 8", 31 | 0x603:"Windows 8.1", 32 | 0xa00:"Windows 10", 33 | } 34 | 35 | # http://winforensicaanalysis.googlecode.com/files/jobparse.pl 36 | task_status = { 37 | 0x41300:"Task is ready to run", 38 | 0x41301:"Task is running", 39 | 0x41302:"Task is disabled", 40 | 0x41303:"Task has not run", 41 | 0x41304:"No more scheduled runs", 42 | 0x41305:"Properties not set", 43 | 0x41306:"Last run terminated by user", 44 | 0x41307:"No triggers/triggers disabled", 45 | 0x41308:"Triggers do not have set run times", 46 | } 47 | 48 | weekdays = { 49 | 0x0:"Sunday", 50 | 0x1:"Monday", 51 | 0x2:"Tuesday", 52 | 0x3:"Wednesday", 53 | 0x4:"Thursday", 54 | 0x5:"Friday", 55 | 0x6:"Saturday", 56 | } 57 | 58 | months = { 59 | 0x1:"Jan", 60 | 0x2:"Feb", 61 | 0x3:"Mar", 62 | 0x4:"Apr", 63 | 0x5:"May", 64 | 0x6:"Jun", 65 | 0x7:"Jul", 66 | 0x8:"Aug", 67 | 0x9:"Sep", 68 | 0xa:"Oct", 69 | 0xb:"Nov", 70 | 0xc:"Dec", 71 | } 72 | 73 | # http://msdn.microsoft.com/en-us/library/cc248283%28v=prot.10%29 74 | flags = { 75 | 0x1:"TASK_APPLICATION_NAME", 76 | 0x200000:"TASK_FLAG_RUN_ONLY_IF_LOGGED_ON", 77 | 0x100000:"TASK_FLAG_SYSTEM_REQUIRED", 78 | 0x80000:"TASK_FLAG_RESTART_ON_IDLE_RESUME", 79 | 0x40000:"TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET", 80 | 0x20000:"TASK_FLAG_HIDDEN", 81 | 0x10000:"TASK_FLAG_RUN_ONLY_IF_DOCKED", 82 | 0x80000000:"TASK_FLAG_KILL_IF_GOING_ON_BATTERIES", 83 | 0x40000000:"TASK_FLAG_DONT_START_IF_ON_BATTERIES", 84 | 0x20000000:"TASK_FLAG_KILL_ON_IDLE_END", 85 | 0x10000000:"TASK_FLAG_START_ONLY_IF_IDLE", 86 | 0x4000000:"TASK_FLAG_DISABLED", 87 | 0x2000000:"TASK_FLAG_DELETE_WHEN_DONE", 88 | 0x1000000:"TASK_FLAG_INTERACTIVE", 89 | } 90 | 91 | # http://msdn.microsoft.com/en-us/library/cc248286%28v=prot.10%29.aspx 92 | priorities = { 93 | 0x20000000:"NORMAL_PRIORITY_CLASS", 94 | 0x40000000:"IDLE_PRIORITY_CLASS", 95 | 0x80000000:"HIGH_PRIORITY_CLASS", 96 | 0x100000:"REALTIME_PRIORITY_CLASS", 97 | } 98 | 99 | class JobDate: 100 | def __init__(self, data, scheduled = False): 101 | # scheduled is the time the job was scheduled to run 102 | self.scheduled = scheduled 103 | self.Year = struct.unpack("H", data[8:10])[0] 138 | self.UUID4 = struct.unpack(">H", data[10:12])[0] 139 | self.UUID5 = struct.unpack(">H", data[12:14])[0] 140 | self.UUID6 = struct.unpack(">H", data[14:16])[0] 141 | 142 | def __repr__(self): 143 | return "{" + "{0:08X}-{1:04X}-{2:04X}-{3:04X}-{4:02X}{5:02X}{6:02X}".format(self.UUID0, self.UUID1, self.UUID2, 144 | self.UUID3, self.UUID4, self.UUID5, self.UUID6) + "}" 145 | 146 | # http://msdn.microsoft.com/en-us/library/cc248285%28PROT.10%29.aspx 147 | class Job: 148 | def __init__(self, data): 149 | ''' 150 | Fixed length section 151 | http://msdn.microsoft.com/en-us/library/cc248286%28v=prot.13%29.aspx 152 | ''' 153 | self.ProductInfo = struct.unpack("I", data[32:36])[0] 163 | self.MaxRunTime = struct.unpack("I", data[48:52])[0] 167 | self.RunDate = JobDate(data[52:68]) 168 | ''' 169 | Variable length section 170 | http://msdn.microsoft.com/en-us/library/cc248287%28v=prot.10%29.aspx 171 | ''' 172 | self.RunningInstanceCount = struct.unpack(" 0: 176 | self.Name = data[72:self.cursor].replace('\x00', "") 177 | self.ParameterSize = struct.unpack(" 0: 181 | self.Parameter = data[self.cursor:self.cursor + self.ParameterSize * 2].replace("\x00", "") 182 | self.cursor += (self.ParameterSize * 2) 183 | self.WorkingDirectorySize = struct.unpack(" 0: 187 | self.WorkingDirectory = data[self.cursor:self.cursor + (self.WorkingDirectorySize * 2)].replace('\x00', "") 188 | self.cursor += (self.WorkingDirectorySize * 2) 189 | self.UserSize = struct.unpack(" 0: 193 | self.User = data[self.cursor:self.cursor + self.UserSize * 2].replace("\x00", "") 194 | self.cursor += (self.UserSize * 2) 195 | self.CommentSize = struct.unpack(" 0: 199 | self.Comment = data[self.cursor:self.cursor + self.CommentSize * 2].replace("\x00", "") 200 | self.cursor += self.CommentSize * 2 201 | # this is probably User Data + Reserved Data: 202 | self.UserData = data[self.cursor:self.cursor + 18] 203 | self.cursor += 18 204 | # This isn't really documented, but this is the time the job was scheduled to run: 205 | self.ScheduledDate = JobDate(data[self.cursor:self.cursor + 20], scheduled = True) 206 | 207 | def _get_job_info(self): 208 | lines = [] 209 | lines.append("Product Info: {0}".format(products.get(self.ProductInfo, "Unknown Version"))) 210 | lines.append("File Version: {0}".format(self.FileVersion)) 211 | lines.append("UUID: {0}".format(self.UUID)) 212 | priority = "" 213 | for p in priorities: 214 | if self.Priority & p == p: 215 | priority += priorities[p] + ", " 216 | if priority != "": 217 | lines.append("Priorities: {0}".format(priority.rstrip(", "))) 218 | hours, ms = divmod(self.MaxRunTime, 3600000) 219 | minutes, ms = divmod(ms, 60000) 220 | seconds = ms / 1000 221 | lines.append("Maximum Run Time: {0:02}:{1:02}:{2:02}.{3} (HH:MM:SS.MS)".format(hours, minutes, seconds, ms)) 222 | lines.append("Exit Code: {0}".format(self.ExitCode)) 223 | lines.append("Status: {0}".format(task_status.get(self.Status, "Unknown Status"))) 224 | theflags = "" 225 | for flag in flags: 226 | if self.Flags & flag == flag: 227 | theflags += flags[flag] + ", " 228 | lines.append("Flags: {0}".format(theflags.rstrip(", "))) 229 | lines.append("Date Run: {0}".format(self.RunDate)) 230 | lines.append("Running Instances: {0}".format(self.RunningInstanceCount)) 231 | lines.append("Application: {0}".format(self.Name)) 232 | if self.Parameter != "": 233 | lines.append("Parameters: {0}".format(self.Parameter)) 234 | lines.append("Working Directory: {0}".format(self.WorkingDirectory)) 235 | lines.append("User: {0}".format(self.User)) 236 | lines.append("Comment: {0}".format(self.Comment)) 237 | lines.append("Scheduled Date: {0}".format(self.ScheduledDate)) 238 | return lines 239 | 240 | def __repr__(self): 241 | lines = "" 242 | lines += "Product Info: {0}\n".format(products.get(self.ProductInfo, "None")) 243 | lines += "File Version: {0}\n".format(self.FileVersion) 244 | lines += "UUID: {0}\n".format(self.UUID) 245 | priority = "" 246 | for p in priorities: 247 | if self.Priority & p == p: 248 | priority += priorities[p] + ", " 249 | if priority != "": 250 | lines += "Priorities: {0}\n".format(priority.rstrip(", ")) 251 | hours, ms = divmod(self.MaxRunTime, 3600000) 252 | minutes, ms = divmod(ms, 60000) 253 | seconds = ms / 1000 254 | lines += "Maximum Run Time: {0:02}:{1:02}:{2:02}.{3} (HH:MM:SS.MS)\n".format(hours, minutes, seconds, ms) 255 | lines += "Exit Code: {0}\n".format(self.ExitCode) 256 | lines += "Status: {0}\n".format(task_status.get(self.Status, "Unknown Status")) 257 | theflags = "" 258 | for flag in flags: 259 | if self.Flags & flag == flag: 260 | theflags += flags[flag] + ", " 261 | lines += "Flags: {0}\n".format(theflags.rstrip(", ")) 262 | lines += "Date Run: {0}\n".format(self.RunDate) 263 | lines += "Running Instances: {0}\n".format(self.RunningInstanceCount) 264 | lines += "Application: {0}\n".format(self.Name) 265 | if self.Parameter != "": 266 | lines += "Parameters: {0}\n".format(self.Parameter) 267 | lines += "Working Directory: {0}\n".format(self.WorkingDirectory) 268 | lines += "User: {0}\n".format(self.User) 269 | lines += "Comment: {0}\n".format(self.Comment) 270 | lines += "Scheduled Date: {0}\n".format(self.ScheduledDate) 271 | return lines 272 | 273 | 274 | def usage(): 275 | print "jobparser.py:\n" 276 | print " -f " 277 | print " -d " 278 | 279 | def main(): 280 | file = None 281 | dir = None 282 | try: 283 | opts, args = getopt.getopt(sys.argv[1:], "hf:d:", ["help", "file=", "dir="]) 284 | except getopt.GetoptError, err: 285 | print str(err) 286 | sys.exit(2) 287 | for o, a in opts: 288 | if o in ("-h", "--help"): 289 | usage() 290 | sys.exit(2) 291 | elif o in ("-f", "--file"): 292 | if os.path.isfile(a): 293 | file = open(a, "rb") 294 | else: 295 | print a + " is not a file" 296 | usage() 297 | return 298 | elif o in ("-d", "--dir"): 299 | dir = a 300 | else: 301 | assert False, "unhandled option\n\n" 302 | sys.exit(2) 303 | 304 | if file == None and dir == None: 305 | usage() 306 | return 307 | 308 | if dir != None and os.path.isdir(dir): 309 | for fname in os.listdir(dir): 310 | if fname.endswith(".job") and os.path.isfile(os.path.join(dir, fname)): 311 | file = open(os.path.join(dir, fname), "rb") 312 | try: 313 | job = Job(file.read(0x2000)) 314 | print "*" * 72 315 | print "File: " + os.path.join(dir, fname) 316 | print job 317 | print "*" * 72 318 | except: 319 | print "Unable to process file: " + os.path.join(dir, fname) 320 | 321 | file = None 322 | 323 | 324 | # I'm not sure what's the largest a job file can be, but I'm setting a limit 325 | # just to avoid accidental imports of large files 326 | elif file != None: 327 | data = file.read(0x2000) 328 | job = Job(data) 329 | print job 330 | 331 | if __name__ == "__main__": 332 | main() 333 | 334 | -------------------------------------------------------------------------------- /misc_python/mbr_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import distorm3 3 | import getopt, sys 4 | import hashlib 5 | import struct 6 | 7 | ''' 8 | Author: Gleeda 9 | 10 | $ python mbr_parser.py -f mbr 11 | no frills, prints out to stdout 12 | 13 | This program is free software; you can redistribute it and/or 14 | modify it under the terms of the GNU General Public License 15 | as published by the Free Software Foundation; either version 16 | 2 of the License, or (at your option) any later version. 17 | 18 | Using structures defined in File System Forensic Analysis pg 88+ 19 | boot code is from bytes 0-439 in the partition table 20 | we should dissassemble 21 | 22 | extra partition types taken from Gary Kessler's MBRParser.pl: 23 | http://www.garykessler.net/software/index.html 24 | 25 | ''' 26 | 27 | PartitionTypes = { 28 | 0x00:"Empty", 29 | 0x01:"FAT12,CHS", 30 | 0x04:"FAT16 16-32MB,CHS", 31 | 0x05:"Microsoft Extended", 32 | 0x06:"FAT16 32MB,CHS", 33 | 0x07:"NTFS", 34 | 0x0b:"FAT32,CHS", 35 | 0x0c:"FAT32,LBA", 36 | 0x0e:"FAT16, 32MB-2GB,LBA", 37 | 0x0f:"Microsoft Extended, LBA", 38 | 0x11:"Hidden FAT12,CHS", 39 | 0x14:"Hidden FAT16,16-32MB,CHS", 40 | 0x16:"Hidden FAT16,32MB-2GB,CHS", 41 | 0x18:"AST SmartSleep Partition", 42 | 0x1b:"Hidden FAT32,CHS", 43 | 0x1c:"Hidden FAT32,LBA", 44 | 0x1e:"Hidden FAT16,32MB-2GB,LBA", 45 | 0x27:"PQservice", 46 | 0x39:"Plan 9 partition", 47 | 0x3c:"PartitionMagic recovery partition", 48 | 0x42:"Microsoft MBR,Dynamic Disk", 49 | 0x44:"GoBack partition", 50 | 0x51:"Novell", 51 | 0x52:"CP/M", 52 | 0x63:"Unix System V", 53 | 0x64:"PC-ARMOUR protected partition", 54 | 0x82:"Solaris x86 or Linux Swap", 55 | 0x83:"Linux", 56 | 0x84:"Hibernation", 57 | 0x85:"Linux Extended", 58 | 0x86:"NTFS Volume Set", 59 | 0x87:"NTFS Volume Set", 60 | 0x9f:"BSD/OS", 61 | 0xa0:"Hibernation", 62 | 0xa1:"Hibernation", 63 | 0xa5:"FreeBSD", 64 | 0xa6:"OpenBSD", 65 | 0xa8:"Mac OSX", 66 | 0xa9:"NetBSD", 67 | 0xab:"Mac OSX Boot", 68 | 0xaf:"MacOS X HFS", 69 | 0xb7:"BSDI", 70 | 0xb8:"BSDI Swap", 71 | 0xbb:"Boot Wizard hidden", 72 | 0xbe:"Solaris 8 boot partition", 73 | 0xd8:"CP/M-86", 74 | 0xde:"Dell PowerEdge Server utilities (FAT fs)", 75 | 0xdf:"DG/UX virtual disk manager partition", 76 | 0xeb:"BeOS BFS", 77 | 0xee:"EFI GPT Disk", 78 | 0xef:"EFI System Parition", 79 | 0xfb:"VMWare File System", 80 | 0xfc:"VMWare Swap", 81 | } 82 | 83 | class PartitionEntry: 84 | def __init__(self, data): 85 | self.BootableFlag = struct.unpack("= 512: 114 | self.PartitionTable = PartitionTable(data[440:]) 115 | 116 | def disassembly_only(self): 117 | lines = [] 118 | full = hashlib.md5() 119 | full.update(self.BootCode) 120 | partial = self.BootCode 121 | p = hashlib.md5() 122 | 123 | iterable = distorm3.DecodeGenerator(0, self.BootCode, distorm3.Decode16Bits) 124 | ret = "" 125 | for (offset, size, instruction, hexdump) in iterable: 126 | ret += "0x%.8x: %-32s %s\n" % (offset, hexdump, instruction) 127 | if instruction == "RET": 128 | partial = self.BootCode[0:offset + size] 129 | hexstuff = "\n" + "\n".join(["{0:#010x}: {1:<48} {2}".format(o, h, ''.join(c)) for o, h, c in self.Hexdump(self.BootCode[offset + size:], offset + size)]) 130 | ret += hexstuff 131 | break 132 | p.update(partial) 133 | lines.append("Bootcode md5 (up to RET): {0}".format(p.hexdigest())) 134 | lines.append("Full Bootcode md5: {0}\n".format(full.hexdigest())) 135 | lines.append(ret) 136 | return lines 137 | 138 | 139 | def print_self(self): 140 | if self.disonly: 141 | return self.disassembly_only() 142 | lines = [] 143 | E0 = self.process_entry(self.PartitionTable.Entry0) 144 | E1 = self.process_entry(self.PartitionTable.Entry1) 145 | E2 = self.process_entry(self.PartitionTable.Entry2) 146 | E3 = self.process_entry(self.PartitionTable.Entry3) 147 | 148 | lines.append("Disk signature: {0:02x}-{1:02x}-{2:02x}-{3:02x}\n".format(self.PartitionTable.DiskSignature0, 149 | self.PartitionTable.DiskSignature1, 150 | self.PartitionTable.DiskSignature2, 151 | self.PartitionTable.DiskSignature3)) 152 | 153 | lines += self.disassembly_only() 154 | 155 | lines.append("===== Partition Table #1 =====\n{0}\n".format(E0)) 156 | lines.append("===== Partition Table #2 =====\n{0}\n".format(E1)) 157 | lines.append("===== Partition Table #3 =====\n{0}\n".format(E2)) 158 | lines.append("===== Partition Table #4 =====\n{0}\n".format(E3)) 159 | lines.append("Valid Signature (0xaa55): {0} => (0x{1:x})\n".format(self.PartitionTable.Signature == 0xaa55, self.PartitionTable.Signature)) 160 | 161 | return lines 162 | 163 | def Hexdump(self, data, given_offset = 0, width = 16): 164 | for offset in xrange(0, len(data), width): 165 | row_data = data[offset:offset + width] 166 | translated_data = [x if ord(x) < 127 and ord(x) > 32 else "." for x in row_data] 167 | hexdata = " ".join(["{0:02x}".format(ord(x)) for x in row_data]) 168 | 169 | yield offset + given_offset, hexdata, translated_data 170 | 171 | def get_value(self, char): 172 | padded = "\x00\x00\x00" + str(char) 173 | val = int(struct.unpack('>I', padded)[0]) 174 | return val 175 | 176 | def get_type(self, PartitionType): 177 | type = PartitionTypes.get(PartitionType, "Invalid") 178 | return type 179 | 180 | def get_sector(self, raw_sector): 181 | return raw_sector % 64 182 | 183 | def get_cylinder(self, raw_sector, raw_cylinder): 184 | return (raw_sector - self.get_sector(raw_sector)) * 4 + raw_cylinder 185 | 186 | def process_entry(self, entry): 187 | processed_entry = "" 188 | bootable = self.get_value(entry.BootableFlag) 189 | type = self.get_type(self.get_value(entry.PartitionType)) 190 | processed_entry = "Boot flag: {0:#x} {1}\n".format(bootable, "(Bootable)" if bootable == 0x80 else '') 191 | processed_entry += "Partition type: {0:#x} ({1})\n".format(self.get_value(entry.PartitionType), type) 192 | processed_entry += "Starting Sector (LBA): {0:#x} ({0})\n".format(entry.StartLBA) 193 | processed_entry += "Starting CHS: Cylinder: {2} Head: {0} Sector: {1}\n".format(entry.StartCHS0, 194 | self.get_sector(entry.StartCHS1), 195 | self.get_cylinder(entry.StartCHS1, entry.StartCHS2)) 196 | processed_entry += "Ending CHS: Cylinder: {2} Head: {0} Sector: {1}\n".format(entry.EndCHS0, 197 | self.get_sector(entry.EndCHS1), 198 | self.get_cylinder(entry.EndCHS1, entry.EndCHS2)) 199 | processed_entry += "Size in sectors: {0:#x} ({0})\n\n".format(entry.SizeInSectors) 200 | return processed_entry 201 | 202 | def usage(): 203 | print "mbr_parser.py:\n" 204 | print " -f " 205 | 206 | def main(): 207 | file = None 208 | output = sys.stdout 209 | try: 210 | opts, args = getopt.getopt(sys.argv[1:], "hf:", ["help", "file="]) 211 | except getopt.GetoptError, err: 212 | print str(err) 213 | sys.exit(2) 214 | for o, a in opts: 215 | if o in ("-h", "--help"): 216 | usage() 217 | sys.exit(2) 218 | elif o in ("-f", "--file"): 219 | file = open(a,'rb') 220 | else: 221 | assert False, "unhandled option\n\n" 222 | sys.exit(2) 223 | 224 | if file == None: 225 | usage() 226 | return 227 | 228 | data = file.read(512) 229 | if len(data) == 512: 230 | myMBR = MBRParser(data) 231 | elif len(data) == 440: 232 | myMBR = MBRParser(data, True) 233 | else: 234 | print "MBR file too small" 235 | return 236 | 237 | lines = myMBR.print_self() 238 | for l in lines: 239 | print l 240 | 241 | if __name__ == "__main__": 242 | main() 243 | -------------------------------------------------------------------------------- /misc_python/opswat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #Author: Gleeda 3 | # 4 | #opswat.py 5 | # takes in a file or directory of files to upload to opswat and prints output 6 | # post_multipart, encode_multipart_formdata and get_content_type taken from MHL's avsubmit.py 7 | # 8 | import sys, os, getopt 9 | import urllib, urllib2, urlparse 10 | import json 11 | import httplib, mimetypes 12 | import time 13 | import sqlite3 14 | 15 | # change these: 16 | selector = "http://10.xxx.xxx.xxx/opswat_scan.php" 17 | host = "10.xxx.xxx.xxx" 18 | DBNAME = None 19 | 20 | def usage(): 21 | print 'opswat.py:' 22 | print ' - takes in a file or directory of files to upload to opswat and prints output' 23 | print '\t-h, --help : print help message' 24 | print '\t-d, --directory : directory with files to upload to opswat' 25 | print '\t-f, --file : file to upload to opswat' 26 | print '\t-q, --db : sqlite3 output file' 27 | print '\t-o, --output : output file\n' 28 | 29 | def init(DBNAME): 30 | if DBNAME == None: 31 | print "can't initialize DB!" 32 | return 33 | 34 | conn = sqlite3.connect(DBNAME) 35 | cur = conn.cursor() 36 | try: 37 | cur.execute("select * from opswat") 38 | except sqlite3.OperationalError: 39 | cur.executescript( 40 | ''' 41 | CREATE TABLE opswat ( 42 | id INTEGER PRIMARY KEY, 43 | artifact TEXT, 44 | file TEXT, 45 | md5 TEXT, 46 | sha1 TEXT, 47 | start TEXT, 48 | end TEXT, 49 | threat TEXT 50 | ); 51 | ''') 52 | conn.commit() 53 | try: 54 | cur.execute("select * from opswat_avscans") 55 | except sqlite3.OperationalError: 56 | cur.executescript( 57 | ''' 58 | CREATE TABLE opswat_avscans ( 59 | id INTEGER PRIMARY KEY, 60 | oid INTEGER, 61 | avname TEXT, 62 | version TEXT, 63 | versiondate TEXT, 64 | scanresult TEXT, 65 | threat TEXT, 66 | avdefsignature TEXT, 67 | avdefversion TEXT 68 | ); 69 | ''') 70 | conn.commit() 71 | try: 72 | cur.execute("select * from artifacts") 73 | except sqlite3.OperationalError: 74 | cur.executescript( 75 | ''' 76 | CREATE TABLE artifacts ( 77 | id INTEGER PRIMARY KEY, 78 | artifact TEXT, 79 | file TEXT, 80 | source TEXT 81 | ); 82 | ''') 83 | conn.close() 84 | 85 | 86 | def post_multipart(host, selector, fields, files): 87 | """ 88 | Post fields and files to an http host as multipart/form-data. 89 | fields is a sequence of (name, value) elements for regular form fields. 90 | files is a sequence of (name, filename, value) elements for data to be uploaded as files 91 | Return the server's response page. 92 | """ 93 | content_type, body = encode_multipart_formdata(fields, files) 94 | h = httplib.HTTP(host) 95 | h.putrequest('POST', selector) 96 | h.putheader('content-type', content_type) 97 | h.putheader('content-length', str(len(body))) 98 | h.endheaders() 99 | h.send(body) 100 | errcode, errmsg, headers = h.getreply() 101 | return h.file.read() 102 | 103 | def encode_multipart_formdata(fields, files): 104 | """ 105 | fields is a sequence of (name, value) elements for regular form fields. 106 | files is a sequence of (name, filename, value) elements for data to be uploaded as files 107 | Return (content_type, body) ready for httplib.HTTP instance 108 | """ 109 | BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' 110 | CRLF = '\r\n' 111 | L = [] 112 | for (key, value) in fields: 113 | L.append('--' + BOUNDARY) 114 | L.append('Content-Disposition: form-data; name="%s"' % key) 115 | L.append('') 116 | L.append(value) 117 | for (key, filename, value) in files: 118 | L.append('--' + BOUNDARY) 119 | L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) 120 | L.append('Content-Type: %s' % get_content_type(filename)) 121 | L.append('') 122 | L.append(value) 123 | L.append('--' + BOUNDARY + '--') 124 | L.append('') 125 | body = CRLF.join(L) 126 | content_type = 'multipart/form-data; boundary=%s' % BOUNDARY 127 | return content_type, body 128 | 129 | def get_content_type(filename): 130 | return mimetypes.guess_type(filename)[0] or 'application/octet-stream' 131 | 132 | 133 | def process(outfd, file_to_send): 134 | global DBNAME 135 | if DBNAME != None: 136 | return process_sql(outfd, file_to_send) 137 | f = open(file_to_send, "r") 138 | file_contents = f.read() 139 | f.close() 140 | 141 | files = [("file", os.path.basename(file_to_send), file_contents)] 142 | fields = [("uploadedFile", file_to_send), ("filename", os.path.basename(file_to_send))] 143 | 144 | json = post_multipart(host, selector, fields, files) 145 | 146 | if json.find("suspiciousfile") == -1: 147 | print "Unable to submit sample" 148 | print json 149 | return -1 150 | 151 | jsondict = simplejson.loads(json) 152 | for key, val in jsondict.items(): 153 | outfd.write("*" * 72 + "\n") 154 | outfd.write("{0:30} {1}\n".format("Filename:", os.path.basename(file_to_send))) 155 | outfd.write("{0:30} {1}\n".format("md5:", val['md5'])) 156 | outfd.write("{0:30} {1}\n".format("sha1:", val['sha1'])) 157 | outfd.write("{0:30} {1}\n".format("Start Time:", val['starttime'])) 158 | outfd.write("{0:30} {1}\n".format("End Time:", val['endtime'])) 159 | outfd.write("{0:30} {1}\n".format("Final Result:", val['finalresult'])) 160 | if val['finalthreatfound'] == "": 161 | val['finalthreatfound'] = "None" 162 | outfd.write("{0:30} {1}\n".format("Final Threat Found:", val['finalthreatfound'])) 163 | outfd.write("*" * 72 + "\n") 164 | if val['finalthreatfound'] == "None": 165 | continue 166 | for item in val['avresults']: 167 | outfd.write("{0:30} {1}\n".format("AV Name:", item['avname'] + " " + item['avversion'] + " " + item['avdefversiondate'])) 168 | if item['scanresult'] == 'Failed': 169 | item['scanresult'] = item['scanresult'].upper() 170 | outfd.write("{0:30} {1}\n".format("Scan Result:", item['scanresult'])) 171 | if item['threatsfound'] == "": 172 | item['threatsfound'] = "None" 173 | else: 174 | item['threatsfound'] += " [!!]" 175 | outfd.write("{0:30} {1}\n".format("Threats Found:", item['threatsfound'])) 176 | outfd.write("{0:30} {1}\n".format("AV DefSignature/DefVersion:", item['avdefsignature'] + "/" + item['avdefversion'])) 177 | outfd.write("-" * 72 + "\n") 178 | outfd.write("\n\n") 179 | return 0 180 | 181 | def process_sql(outfd, file_to_send): 182 | global DBNAME 183 | conn = sqlite3.connect(DBNAME) 184 | cur = conn.cursor() 185 | 186 | f = open(file_to_send, "r") 187 | file_contents = f.read() 188 | f.close() 189 | 190 | files = [("file", os.path.basename(file_to_send), file_contents)] 191 | fields = [("uploadedFile", file_to_send), ("filename", os.path.basename(file_to_send))] 192 | 193 | json = post_multipart(host, selector, fields, files) 194 | 195 | if json.find("suspiciousfile") == -1: 196 | print "Unable to submit sample" 197 | print json 198 | conn.close() 199 | return -1 200 | 201 | jsondict = simplejson.loads(json) 202 | for key, val in jsondict.items(): 203 | outfd.write("*" * 72 + "\n") 204 | outfd.write("{0:30} {1}\n".format("Filename:", os.path.basename(file_to_send))) 205 | outfd.write("{0:30} {1}\n".format("Final Result:", val['finalresult'])) 206 | if val['finalthreatfound'] == "": 207 | val['finalthreatfound'] = "None" 208 | outfd.write("{0:30} {1}\n".format("Final Threat Found:", val['finalthreatfound'])) 209 | 210 | cur.execute("SELECT COUNT(*) FROM opswat WHERE md5 = ?", [val['md5']]) 211 | count = cur.fetchone()[0] 212 | if count > 0: 213 | outfd.write("Sample {0} already exists in DB... not dumped\n".format(val['md5'])) 214 | outfd.write("*" * 72 + "\n") 215 | continue 216 | outfd.write("*" * 72 + "\n") 217 | 218 | cur.execute("INSERT INTO opswat VALUES(null, ?,?,?,?,?,?,?)", ("", os.path.basename(file_to_send), 219 | val['md5'], val['sha1'], val['starttime'], val['endtime'], val['finalthreatfound'])) 220 | conn.commit() 221 | cur.execute("SELECT id FROM opswat WHERE md5 = ?", [val['md5']]) 222 | id = cur.fetchone()[0] 223 | 224 | if val['finalthreatfound'] == "None": 225 | continue 226 | 227 | cur.execute("SELECT count(*) FROM artifacts WHERE file LIKE ?", [os.path.basename(file_to_send)]) 228 | count = cur.fetchone()[0] 229 | if count == 0: 230 | try: 231 | if os.path.basename(file_to_send).find(".exe") != -1 and q[0].find(".dmp") == -1: 232 | cur.execute("SELECT pname FROM procdump WHERE dump_file = ?", [os.path.basename(file_to_send)]) 233 | artifact = "Executable: " + cur.fetchone()[0] 234 | elif os.path.basename(file_to_send).find(".dll") != -1: 235 | cur.execute("SELECT path FROM dlldump WHERE dump_file = ?", [os.path.basename(file_to_send)]) 236 | artifact = "DLL: " + cur.fetchone()[0] 237 | elif os.path.basename(file_to_send).find(".dmp") != -1: 238 | cur.execute("SELECT pname FROM vaddump WHERE dump_file like ?", [os.path.basename(file_to_send)]) 239 | artifact = "Vaddump from process: " + cur.fetchone()[0] 240 | else: 241 | cur.execute("SELECT name FROM moddump WHERE dump_file = ?", [os.path.basename(file_to_send)]) 242 | artifact = "Module: " + cur.fetchone()[0] 243 | cur.execute("INSERT INTO artifacts VALUES(null,?,?,?)", (artifact, os.path.basename(file_to_send), "opswat")) 244 | conn.commit() 245 | except: 246 | pass 247 | 248 | for item in val['avresults']: 249 | if item['scanresult'] == 'Failed': 250 | item['scanresult'] = item['scanresult'].upper() 251 | if item['threatsfound'] == "": 252 | item['threatsfound'] = "None" 253 | cur.execute("INSERT INTO opswat_avscans VALUES(null, ?,?,?,?,?,?,?,?)", (id, item['avname'], item['avversion'], item['avdefversiondate'], 254 | item['scanresult'], item['threatsfound'], item['avdefsignature'], item['avdefversion'])) 255 | conn.commit() 256 | 257 | outfd.write("*" * 72 + "\n\n") 258 | conn.close() 259 | return 0 260 | 261 | 262 | def main(): 263 | try: 264 | opts, args = getopt.getopt(sys.argv[1:], "hf:d:o:q:", ["help", "file=", "output=", "directory=", "db="]) 265 | except getopt.GetoptError, err: 266 | print str(err) 267 | sys.exit(2) 268 | 269 | file_to_send = None 270 | dir = None 271 | outfd = sys.stdout 272 | global DBNAME 273 | 274 | for o, a in opts: 275 | if o in ("-h", "--help"): 276 | usage() 277 | sys.exit(2) 278 | elif o in ("-f", "--file"): 279 | file_to_send = a 280 | elif o in ("-d", "--directory"): 281 | dir = a 282 | elif o in ("-o", "--output"): 283 | outfd = open(a,'w') 284 | elif o in ("-q", "--db"): 285 | DBNAME = a 286 | else: 287 | assert False, "unhandled option\n\n" 288 | usage() 289 | return 290 | 291 | if DBNAME != None: 292 | init(DBNAME) 293 | 294 | 295 | if dir != None: 296 | if not os.path.exists(dir): 297 | print "Directory " + dir + " not found!\n" 298 | usage() 299 | return 300 | elif not os.path.isdir(dir): 301 | print dir + " is not a directory!\n" 302 | usage() 303 | return 304 | for subdir, dirs, files in os.walk(dir): 305 | for file in files: 306 | if os.path.exists(dir + "/" + file): 307 | print "Sending " + dir + "/" + file + "....." 308 | status = process(outfd, dir + "/" + file) 309 | if status == -1: 310 | #we're only gonna give each file 1 extra try... 311 | print "Got an error, sleeping it off before trying again..." 312 | time.sleep(10) 313 | print "Awake - resending file " + dir + "/" + file 314 | if process(outfd, dir + "/" + file): 315 | print "Another failure, try again later..." 316 | return 317 | 318 | if file_to_send == None: 319 | print "You must specify a file or directory of files to send to opswat!\n" 320 | usage() 321 | return 322 | elif not os.path.exists(file_to_send): 323 | print "File " + file_to_send + " not found!\n" 324 | usage() 325 | return 326 | elif os.path.isdir(file_to_send): 327 | print "File " + file_to_send + " is a directory, not a file\n" 328 | usage() 329 | return 330 | 331 | status = process(outfd, file_to_send) 332 | if status == -1: 333 | print "Got an error, sleeping it off before trying again..." 334 | time.sleep(5) 335 | print "Awake - resending file " + file_to_send 336 | if process(outfd, file_to_send): 337 | print "Another failure, try again later..." 338 | 339 | if __name__ == "__main__": 340 | main() 341 | 342 | -------------------------------------------------------------------------------- /misc_python/parsesummary.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sys 3 | import os 4 | 5 | """ 6 | Author: Gleeda 7 | 8 | This program is free software; you can redistribute it and/or 9 | modify it under the terms of the GNU General Public License 10 | as published by the Free Software Foundation; either version 11 | 2 of the License, or (at your option) any later version. 12 | 13 | parsesummary.py [summary_file] 14 | Parses summary files created from the dumpfiles plugin 15 | 16 | """ 17 | 18 | def usage(name): 19 | print "{0} [summary_file]".format(name) 20 | 21 | def main(): 22 | try: 23 | summary = sys.argv[1] 24 | if os.path.isfile(summary): 25 | f = open(summary, "r") 26 | else: 27 | print summary, "is not a file!" 28 | usage(sys.argv[0]) 29 | return 30 | except: 31 | usage(sys.argv[0]) 32 | return 33 | 34 | heading = "*" * 80 35 | for line in f.readlines(): 36 | print heading 37 | item = json.loads(line.strip()) 38 | print "File: {0} -> {1}".format(item["name"], item["ofpath"]) 39 | print "\tPID: {0}".format(item["pid"]) 40 | print "\t_FILE_OBJECT offset: 0x{0:x}".format(item["fobj"]) 41 | print "\tType: {0}".format(item["type"]) 42 | vacbary = item.get("vacbary", []) 43 | if item["type"] == "SharedCacheMap" and vacbary != []: 44 | for vacb in vacbary: 45 | print "\tSize: {0}".format(vacb["size"]) 46 | present = vacb.get("present", None) 47 | padding = vacb.get("pad", None) 48 | if present != None: 49 | print "\tPresent Pages:" 50 | for page in present: 51 | print "\t\tOffset(V): 0x{0:x}, Length: {1}".format(page[0], page[1]) 52 | 53 | else: 54 | present = item.get("present", None) 55 | if present != None: 56 | print "\tPresent Pages:" 57 | if item["type"] != "SharedCacheMap": 58 | for page in present: 59 | print "\t\tOffset(P) 0x{0:x} FileOffset: 0x{1:x}, Size: {2}".format(page[0], page[1], page[2]) 60 | padding = item.get("pad", None) 61 | if padding != None: 62 | print "\tPadding:" 63 | for pad in padding: 64 | print "\t\tFileOffset: 0x{0:x} x 0x{1:x}".format(pad[0], pad[1]) 65 | print heading 66 | 67 | if __name__ == "__main__": 68 | main() 69 | -------------------------------------------------------------------------------- /misc_python/printkey.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from Registry import Registry 3 | 4 | ''' 5 | Author: Gleeda 6 | 7 | Modified from one of Willi Ballenthin's sample codes 8 | 9 | usage: python printkey.py [registry] [key] 10 | ''' 11 | 12 | reg = Registry.Registry(sys.argv[1]) 13 | k = sys.argv[2] 14 | 15 | reg_keys1 = [] 16 | 17 | def rec(key, reg_keys1): 18 | reg_keys1.append(key) 19 | 20 | for subkey in key.subkeys(): 21 | rec(subkey, reg_keys1) 22 | 23 | 24 | f = sys.stdout 25 | rec(reg.root(), reg_keys1) 26 | def process_key(thekey, reg_keys1): 27 | for key in reg_keys1: 28 | if key.path().lower().endswith(thekey.lower()): 29 | f.write("*"*72 + "\n") 30 | f.write(key.path().lower()) 31 | for value in key.values(): 32 | f.write("\nVALUENAME: ") 33 | f.write(value.name()) 34 | f.write("\nVALUE: ") 35 | try: 36 | print value.value() 37 | except: 38 | item = value.value() 39 | v = ''.join([str(c) for c in item if (ord(c) > 31 or ord(c) == 9) and ord(c) <= 126]) 40 | print v.decode("ascii", "ignore") 41 | 42 | f.write("\nSubkeys: \n") 43 | for subkey in key.subkeys(): 44 | f.write(subkey.path() + "\n") 45 | f.write("*"*72 + "\n") 46 | 47 | 48 | print "Processing", sys.argv[1] 49 | process_key(k, reg_keys1) 50 | -------------------------------------------------------------------------------- /misc_python/pushtimeline.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import getopt 4 | import sys, os 5 | 6 | ''' 7 | Author: Jamie Levy (Gleeda) 8 | 9 | This program is free software; you can redistribute it and/or 10 | modify it under the terms of the GNU General Public License 11 | as published by the Free Software Foundation; either version 12 | 2 of the License, or (at your option) any later version. 13 | 14 | pushtimeline.py: Push timeline information to Kibana 15 | -h, --help: Print out this message 16 | -d, --delete [http://localhost:9200/INDEX]: Delete INDEX 17 | -m, --machine [MACHINE]: Machine identifier 18 | -u, --url [KIBANA URL]: Default http://localhost:9200 19 | -f, --file [FILE]: File to read time data from 20 | -b, --body: File in bodyfile format 21 | 22 | ''' 23 | 24 | url="http://localhost:9200/" 25 | mapping = "{0}allmachines/_mapping/timeline".format(url) 26 | mytype = '{"timeline": {"properties": {"timestamp": {"type":"date","format": "epoch_second"}}}}' 27 | 28 | def usage(): 29 | print "pushtimeline.py: Push timeline information to Kibana" 30 | print "\t-h, --help: Print out this message" 31 | print "\t-d, --delete [http://localhost:9200/INDEX]: Delete INDEX" 32 | print "\t-m, --machine [MACHINE]: Machine identifier" 33 | print "\t-u, --url [KIBANA URL]: Default http://localhost:9200" 34 | print "\t-f, --file [FILE]: File to read time data from" 35 | print "\t-b, --body: File in bodyfile format" 36 | 37 | def fixup1(item): 38 | item = item.replace("\\", "\\\\") 39 | item = item.replace("\"", "\\\"") 40 | item = item.replace(":", "\\\\:") 41 | #item = item.replace("$", "\\\\$") 42 | return item 43 | 44 | def fixtime(stamp): 45 | if stamp == "-" or int(stamp) == -1: 46 | return 0 47 | stamp = stamp if int(stamp) < 2147483647 else 0 48 | return stamp 49 | 50 | def main(): 51 | try: 52 | opts, args = getopt.getopt(sys.argv[1:], "hbu:m:f:d:", ["help", "delete=", "url=", "machine=", "file=", "body"]) 53 | except getopt.GetoptError, err: 54 | print str(err) 55 | sys.exit(2) 56 | 57 | file = None 58 | machine = None 59 | url="http://localhost:9200/" 60 | mapping = "{0}allmachines/_mapping/timeline".format(url) 61 | mytype = '{"timeline": {"properties": {"timestamp": {"type":"date","format": "epoch_second"}}}}' 62 | body = False 63 | for o, a in opts: 64 | if o in ("-h", "--help"): 65 | usage() 66 | return 67 | if o in ("-b", "--body"): 68 | body = True 69 | elif o in ("-u", "--url"): 70 | url = "{0}".format(a.lower()) 71 | elif o in ("-d", "--delete"): 72 | r = requests.delete(a.lower()) 73 | print r.content 74 | return 75 | elif o in ("-m", "--machine"): 76 | machine = a.lower() 77 | url = "{0}{1}".format(url, machine) 78 | r = requests.put(url) 79 | mapping = "{0}/_mapping/timeline".format(url) 80 | url = "{0}/timeline?pretty".format(url) 81 | elif o in ("-f", "--file"): 82 | file = a 83 | else: 84 | assert False, "unhandled option\n\n" 85 | return 86 | 87 | if file == None: 88 | print "You must specify a file!" 89 | usage() 90 | return 91 | 92 | if not os.path.isfile(file): 93 | print "File {0} not found!".format(file) 94 | usage() 95 | return 96 | 97 | print "Url", url 98 | print "Mapping", mapping 99 | print "Machine", machine 100 | r = requests.put(mapping, data=mytype) 101 | print r.content 102 | 103 | 104 | f = open(file, "r") 105 | if not body: 106 | for payload in f.readlines(): 107 | print payload.strip() 108 | print json.dumps(payload.strip()) 109 | r = requests.post(url, data=payload.strip()) 110 | print r.content 111 | else: 112 | bbrace = "{" 113 | ebrace = "}" 114 | for payload in f.readlines(): 115 | print payload.strip() 116 | things = payload.strip().split("|") 117 | #header,accessed, modified, mftaltered, creation 118 | #things[1], things[-4], things[-3], things[-2], things[-1] 119 | accessed = fixtime(things[-4]) 120 | modified = fixtime(things[-3]) 121 | mftaltered = fixtime(things[-2]) 122 | creation = fixtime(things[-1]) 123 | line1 = "{0}\"machine\": \"{1}\", \"timestamp\": {2}, \"timetype\": \"{3}\", \"event\": \"{4}\", \"message\": \"{5}\"{6}".format( 124 | bbrace, 125 | machine, 126 | modified, 127 | "modified", 128 | things[1].split("]")[0] + "]", 129 | fixup1(things[1]), 130 | ebrace) 131 | line2 = "{0}\"machine\": \"{1}\", \"timestamp\": {2}, \"timetype\": \"{3}\", \"event\": \"{4}\", \"message\": \"{5}\"{6}".format( 132 | bbrace, 133 | machine, 134 | accessed, 135 | "accessed", 136 | things[1].split("]")[0] + "]", 137 | fixup1(things[1]), 138 | ebrace) 139 | line3 = "{0}\"machine\": \"{1}\", \"timestamp\": {2}, \"timetype\": \"{3}\", \"event\": \"{4}\", \"message\": \"{5}\"{6}".format( 140 | bbrace, 141 | machine, 142 | mftaltered, 143 | "mftaltered", 144 | things[1].split("]")[0] + "]", 145 | fixup1(things[1]), 146 | ebrace) 147 | line4 = "{0}\"machine\": \"{1}\", \"timestamp\": {2}, \"timetype\": \"{3}\", \"event\": \"{4}\", \"message\": \"{5}\"{6}".format( 148 | bbrace, 149 | machine, 150 | creation, 151 | "creation", 152 | things[1].split("]")[0] + "]", 153 | fixup1(things[1]), 154 | ebrace) 155 | 156 | for item in [line1, line2, line3, line4]: 157 | r = requests.post(url, data=item.strip()) 158 | print r.content 159 | 160 | 161 | 162 | if __name__ == "__main__": 163 | main() 164 | -------------------------------------------------------------------------------- /misc_python/similar_files/setify.py: -------------------------------------------------------------------------------- 1 | # 2 | # Author: Gleeda 3 | # 4 | # setify.py 5 | # takes in a directory of strings files and prints out strings contained in each of them 6 | # 7 | 8 | import os, getopt 9 | import sys 10 | import string 11 | 12 | sensitive = True 13 | 14 | def usage(): 15 | print 'setify.py:' 16 | print ' - takes in a directory containing strings files for executables and outputs unique strings shared by all exes' 17 | print '\t-h, --help : print help message' 18 | print '\t-d, --directory : directory with strings files' 19 | print '\t-i, --insensitive : case insensitive' 20 | print '\t-o, --output : output file\n' 21 | 22 | def printset(theset, output): 23 | for item in theset: 24 | output.write(item + "\n") 25 | 26 | 27 | def process(dir, output): 28 | list_of_sets = {} 29 | global sensitive 30 | 31 | for subdir, dirs, files in os.walk(dir): 32 | for file in files: 33 | if os.path.exists(dir + "/" + file): 34 | temp = set([]) 35 | f = open(dir + "/" + file, 'r') 36 | lines = f.readlines() 37 | f.close() 38 | for line in lines: 39 | if not sensitive: 40 | line = line.lower() 41 | line = line.rstrip("\r\n") 42 | temp.add(line) 43 | list_of_sets[file] = temp 44 | 45 | things = list_of_sets.keys() 46 | 47 | if len(things) == 1: 48 | printset(list_of_sets[things[0]], output) 49 | return 50 | elif len(things) >= 2: 51 | theset = list_of_sets[things[0]] & list_of_sets[things[1]] 52 | else: 53 | print "No files found in directory", dir 54 | return 55 | 56 | for item in list_of_sets: 57 | if item != things[0] and item != things[1]: 58 | theset &= list_of_sets[item] 59 | 60 | printset(theset, output) 61 | 62 | 63 | 64 | def main(): 65 | try: 66 | opts, args = getopt.getopt(sys.argv[1:], "hio:d:", ["help", "insensitive", "output=", "directory="]) 67 | except getopt.GetoptError, err: 68 | print str(err) 69 | sys.exit(-1) 70 | output = sys.stdout 71 | dir = None 72 | global sensitive 73 | for o, a in opts: 74 | if o in ("-h", "--help"): 75 | usage() 76 | sys.exit(2) 77 | elif o in ("-o", "--output"): 78 | output = open(a,'w') 79 | elif o in ("-d", "--directory"): 80 | dir = a 81 | elif o in ("-i", "--insensitive"): 82 | sensitive = False 83 | else: 84 | assert False, "unhandled option\n\n" 85 | sys.exit(-1) 86 | 87 | if dir == None: 88 | usage() 89 | sys.exit(-1) 90 | 91 | process(dir, output) 92 | 93 | if __name__ == "__main__": 94 | main() 95 | -------------------------------------------------------------------------------- /misc_python/similar_files/stringify.bsh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | strings_loc='strings.exe' #change to location of sysinternals strings... 4 | mkdir -p compare 5 | for f in `ls *.exe`; do 6 | wine $strings_loc -n 4 -q "$f" > "compare/$f.strings" 7 | done 8 | -------------------------------------------------------------------------------- /prefetch/pf_baseline.py: -------------------------------------------------------------------------------- 1 | # author: Gleeda 2 | # 3 | # pf_baseline allows one to quickly find uncommon prefetch files by calculating a list from baseline 4 | # prefetch names from known paths. Items that are not in that baseline are printed. 5 | # hashing functions are taken from Yogesh Khatri's EnScript for prefetch 6 | 7 | import ctypes 8 | import getopt, sys, os 9 | import sqlite3 10 | 11 | def usage(): 12 | print 'pf_baseline.py:' 13 | print ' - finds uncommon prefetch files using a base of exes' 14 | print '\t-h, --help : Print this help message' 15 | print '\t-x, --xp : Use prefetch hashing algorithm for XP/2K3 (Default)' 16 | print '\t-v, --vista : Use prefetch hashing algorithm for Vista/2k8/Win7' 17 | print '\t-d, --database : EnCase baseline database' 18 | print '\t (or any sqlite3 db with same schema: SELECT path FROM entries WHERE path LIKE \'%exe\')' 19 | print '\t-l, --location : Directory of prefetch files' 20 | print '\t-n, --numvols : Number of volumes to create baseline for (e.g. -n 3 : harddiskvolume1, harddiskvolume2, harddiskvolume3)' 21 | print '\t-p, --particular : A particular volume to create the baseline for (e.g. -p 2 : harddiskvolume2)\n' 22 | print "Example usage:\n\t$ python pf_baseline.py -d baseline.db -v -l /path/to/prefetch/files\n" 23 | 24 | class PFBaseline: 25 | 26 | def __init__(self, XP = True, numvolumes = 1): 27 | self.volume = "\\device\\harddiskvolume" 28 | self.XP = XP 29 | self.numvolumes = numvolumes 30 | self.prefetches = [] 31 | # appending other known files in Windows\Prefetch: 32 | self.prefetches.append("NTOSBOOT-B00DFAAD.pf") 33 | self.prefetches.append("Layout.ini") 34 | 35 | 36 | def GenOneVolHashes(self, cmds, volume = 1): 37 | for c in cmds: 38 | exe = c.split("\\")[-1].upper() 39 | cmd = self.volume + str(volume) + "\\" + c 40 | if self.XP: 41 | item = "{0}-{1:08X}.pf".format(exe, self.generateXpHash(cmd.upper())) 42 | else: 43 | item = "{0}-{1:08X}.pf".format(exe, self.generateVistaHash(cmd.upper())) 44 | if item not in self.prefetches: 45 | self.prefetches.append(item) 46 | 47 | def GenAllVolHashes(self, cmds): 48 | for c in cmds: 49 | exe = c.split("\\")[-1].upper() 50 | for i in xrange(1, self.numvolumes + 1): 51 | cmd = self.volume + str(i) + "\\" + c 52 | if self.XP: 53 | item = "{0}-{1:08X}.pf".format(exe, self.generateXpHash(cmd.upper())) 54 | else: 55 | item = "{0}-{1:08X}.pf".format(exe, self.generateVistaHash(cmd.upper())) 56 | if item not in self.prefetches: 57 | self.prefetches.append(item) 58 | 59 | def generateXpHash(self, cmd): 60 | hash = 0 61 | uni = unicode(cmd) 62 | for i in range(len(uni)): 63 | num = ord(uni[i]) 64 | if (num > 255): 65 | hash = ctypes.c_int32(37 * ((37 * hash) + (num / 256)) + (num % 256)).value 66 | else: 67 | hash = ctypes.c_int32(37 * ((37 * hash) + num)).value 68 | hash *= 314159269 69 | hash = ctypes.c_int32(hash).value 70 | if hash < 0: 71 | hash *= -1 72 | hash %= 1000000007 73 | return ctypes.c_uint32(hash).value 74 | 75 | def generateVistaHash(self, cmd): 76 | hash = 314159 77 | uni = unicode(cmd) 78 | for i in range(len(uni)): 79 | num = ord(uni[i]) 80 | if (num > 255): 81 | hash = ctypes.c_int32(37 * ((37 * hash) + (num / 256)) + (num % 256)).value 82 | else: 83 | hash = ctypes.c_int32(37 * ((37 * hash) + num)).value 84 | return ctypes.c_uint32(hash).value 85 | 86 | def main(): 87 | try: 88 | opts, args = getopt.getopt(sys.argv[1:], "hvxn:d:p:l:", ["help", "location=", "database=", "numvols=", "particular=", "xp", "vista"]) 89 | except getopt.GetoptError, err: 90 | print str(err) 91 | sys.exit(2) 92 | 93 | xp = True 94 | vol = None 95 | vols = None 96 | base = None 97 | dir = None 98 | for o, a in opts: 99 | if o in ("-h", "--help"): 100 | usage() 101 | return 102 | elif o in ("-x", "--xp"): 103 | xp = True 104 | elif o in ("-v", "--vista"): 105 | xp = False 106 | elif o in ("-d", "--database"): 107 | base = a 108 | elif o in ("-n", "--numvols"): 109 | vols = a 110 | elif o in ("-p", "--particular"): 111 | vol = a 112 | elif o in ("-l", "--location"): 113 | dir = a 114 | else: 115 | assert False, "unhandled option\n\n" 116 | return 117 | 118 | if not base: 119 | print "No database specified!" 120 | usage() 121 | return 122 | 123 | if not dir: 124 | print "No directory of prefetch files specified!" 125 | usage() 126 | return 127 | 128 | cmds = [] 129 | rc = sqlite3.connect(base) 130 | cur = rc.cursor() 131 | q = "SELECT path FROM entries WHERE path LIKE '%exe'" 132 | for i in cur.execute(q): 133 | cmds.append(i[0]) 134 | rc.close() 135 | 136 | if vols != None: 137 | p = PFBaseline(XP = xp, numvolumes = int(vols)) 138 | p.GenAllVolHashes(cmds = cmds) 139 | elif vol != None: 140 | p = PFBaseline(XP = xp) 141 | p.GenOneVolHashes(cmds = cmds, volume = int(vol)) 142 | else: 143 | p = PFBaseline(XP = xp) 144 | p.GenOneVolHashes(cmds = cmds) 145 | 146 | os.chdir(dir) 147 | for fname in os.listdir(dir): 148 | if os.path.isfile(os.path.join(dir, fname)): 149 | if fname not in p.prefetches: 150 | print fname 151 | 152 | if __name__ == "__main__": 153 | main() 154 | -------------------------------------------------------------------------------- /prefetch/prefetch_hash.py: -------------------------------------------------------------------------------- 1 | # author: Gleeda 2 | # port of hashing functions from Yogesh Khatri's EnScript 3 | # for prefetch 4 | 5 | import ctypes 6 | import getopt, sys 7 | 8 | def usage(): 9 | print 'prefetch_hash.py:' 10 | print ' - generates the name of the prefetch file given a kernel path to the program' 11 | print '\t-h, --help : print help message' 12 | print '\t-p, --path : kernel path to a program (not case sensitive)' 13 | print '\t-x, --xp : print the name of a prefetch file for XP/2K3' 14 | print '\t-v, --vista : print the name of a prefetch file for Vista/2k8/Win7\n' 15 | print "Example usage:\n\t$ python prefetch_hash.py -p '\\device\\harddiskvolume1\\windows\\system32\\notepad.exe' -v" 16 | 17 | def generateXpHash(cmd): 18 | hash = 0 19 | uni = unicode(cmd) 20 | for i in range(len(uni)): 21 | num = ord(uni[i]) 22 | if (num > 255): 23 | hash = ctypes.c_int32(37 * ((37 * hash) + (num / 256)) + (num % 256)).value 24 | else: 25 | hash = ctypes.c_int32(37 * ((37 * hash) + num)).value 26 | hash *= 314159269 27 | hash = ctypes.c_int32(hash).value 28 | if hash < 0: 29 | hash *= -1 30 | hash %= 1000000007 31 | return ctypes.c_uint32(hash).value 32 | 33 | def generateVistaHash(cmd): 34 | hash = 314159 35 | uni = unicode(cmd) 36 | for i in range(len(uni)): 37 | num = ord(uni[i]) 38 | if (num > 255): 39 | hash = ctypes.c_int32(37 * ((37 * hash) + (num / 256)) + (num % 256)).value 40 | else: 41 | hash = ctypes.c_int32(37 * ((37 * hash) + num)).value 42 | return ctypes.c_uint32(hash).value 43 | 44 | def main(): 45 | try: 46 | opts, args = getopt.getopt(sys.argv[1:], "hvxp:", ["help", "path=", "xp", "vista"]) 47 | except getopt.GetoptError, err: 48 | print str(err) 49 | sys.exit(2) 50 | 51 | cmd = None 52 | xp = False 53 | vista = False 54 | for o, a in opts: 55 | if o in ("-h", "--help"): 56 | usage() 57 | sys.exit(2) 58 | elif o in ("-x", "--xp"): 59 | xp = True 60 | elif o in ("-v", "--vista"): 61 | vista = True 62 | elif o in ("-p", "--path"): 63 | cmd = a.upper() 64 | else: 65 | assert False, "unhandled option\n\n" 66 | sys.exit(2) 67 | 68 | if cmd == None: 69 | print "You must enter a path!" 70 | usage() 71 | sys.exit(2) 72 | 73 | exe = cmd.split("\\")[-1] 74 | 75 | if xp: 76 | hash = "%X" % generateXpHash(cmd) 77 | print exe + "-" + str(hash) + ".pf" 78 | if vista: 79 | hash = "%X" % generateVistaHash(cmd) 80 | print exe + "-" + str(hash) + ".pf" 81 | if not xp and not vista: 82 | print "No type of OS specified!" 83 | usage() 84 | 85 | if __name__ == "__main__": 86 | main() 87 | --------------------------------------------------------------------------------