├── MassDiffer.py ├── README.md ├── bin_export.idc └── execute.py /MassDiffer.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This software is licensed under a Beerware license. 3 | /* 4 | * ---------------------------------------------------------------------------- 5 | * "THE BEER-WARE LICENSE" (Revision 42): 6 | * wrote this file. As long as you retain this notice you 7 | * can do whatever you want with this stuff. If we meet some day, and you think 8 | * this stuff is worth it, you can buy me a beer in return Debasish Mandal. 9 | * ---------------------------------------------------------------------------- 10 | */ 11 | ''' 12 | 13 | from ctypes import * 14 | import os 15 | from os.path import join 16 | from subprocess import check_output 17 | 18 | import struct 19 | import sqlite3 20 | 21 | ida_32 = r'"C:\Program Files (x86)\IDA 6.95\idaq.exe"' 22 | ida_64 = r'"C:\Program Files (x86)\IDA 6.95\idaq64.exe"' 23 | differ_32 = r'"C:\Program Files (x86)\zynamics\BinDiff 4.2\bin\differ.exe"' 24 | differ_64 = r'"C:\Program Files (x86)\zynamics\BinDiff 4.2\bin\differ64.exe"' 25 | def ParseBinDiff(db_file): 26 | #print '[+] Parsing Bindiff file ',db_file 27 | conn = sqlite3.connect(db_file) 28 | c = conn.cursor() 29 | result = c.execute("SELECT address1, address2, similarity FROM function WHERE similarity != '1'") 30 | buff = '' 31 | for row in result: 32 | if row[0] == row[1]: 33 | buff += hex(row[0]) + '|' 34 | else: 35 | buff += hex(row[0])+ '>' +hex(row[1]) + '|' 36 | return buff 37 | def CheckARC(filepath): 38 | IMAGE_FILE_MACHINE_I386=332 39 | IMAGE_FILE_MACHINE_IA64=512 40 | IMAGE_FILE_MACHINE_AMD64=34404 41 | f=open(filepath, "rb") 42 | s=f.read(2) 43 | if s!="MZ": 44 | print "Not an EXE file" 45 | else: 46 | f.seek(60) 47 | s=f.read(4) 48 | header_offset=struct.unpack(" 2 | static main() 3 | { 4 | Batch(0); 5 | Wait(); 6 | RunPlugin("zynamics_binexport_8", 2 ); 7 | Exit(0); 8 | } -------------------------------------------------------------------------------- /execute.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This software is licensed under a Beerware license. 3 | /* 4 | * ---------------------------------------------------------------------------- 5 | * "THE BEER-WARE LICENSE" (Revision 42): 6 | * wrote this file. As long as you retain this notice you 7 | * can do whatever you want with this stuff. If we meet some day, and you think 8 | * this stuff is worth it, you can buy me a beer in return Debasish Mandal. 9 | * ---------------------------------------------------------------------------- 10 | */ 11 | ''' 12 | 13 | 14 | 15 | import os 16 | import array 17 | from os.path import join 18 | from ctypes import * 19 | from win32com.client import Dispatch 20 | from win32api import GetFileVersionInfo 21 | import csv 22 | from MassDiffer import * 23 | import thread 24 | from time import sleep 25 | 26 | all_packages = ['VMware-workstation-full-12.1.1.exe', 27 | 'VMware-workstation-full-12.5.0.exe', 28 | 'VMware-workstation-full-12.5.1.exe', 29 | 'VMware-workstation-full-12.5.2.exe', 30 | 'VMware-workstation-full-12.5.3.exe', 31 | 'VMware-workstation-full-12.5.4.exe', 32 | 'VMware-workstation-full-12.5.5.exe', 33 | 'VMware-workstation-full-12.5.6.exe'] 34 | 35 | def getFileDescription(windows_exe): 36 | try: 37 | language, codepage = GetFileVersionInfo(windows_exe, '\\VarFileInfo\\Translation')[0] 38 | stringFileInfo = u'\\StringFileInfo\\%04X%04X\\%s' % (language, codepage, "FileDescription") 39 | description = GetFileVersionInfo(windows_exe, stringFileInfo) 40 | except: 41 | description = "unknown" 42 | return description 43 | def get_file_info(path): 44 | ver_parser = Dispatch('Scripting.FileSystemObject') 45 | info = ver_parser.GetFileVersion(path) 46 | #print ver_parser.GetProduct(path) 47 | if info == 'No Version Information Available': 48 | info = None 49 | return info 50 | def isPE(file): 51 | hnd = open(file,"rb") 52 | if hnd.read(2) == "MZ": 53 | return True 54 | else: 55 | return False 56 | def FindAllFilesBINFileInPackage(pack_name): 57 | print '[+] Finding Package Content for ',pack_name 58 | basefilelist = {} 59 | bases = list() 60 | for (dirpath, dirname, filenames) in os.walk(pack_name): 61 | for name in filenames: 62 | a, b = os.path.splitext(join(dirpath,name)) 63 | bases.append((a, b)) 64 | for i in bases: 65 | if isPE(i[0]+i[1]): 66 | full = i[0]+i[1] 67 | basefilelist[full.replace(pack_name+'\\','')] = get_file_info(i[0]+i[1]) 68 | else: 69 | pass 70 | return basefilelist 71 | def ErrorLog(string): 72 | f = open('error.log','a') 73 | f.write(string+'\n\n') 74 | f.close() 75 | def openLog(fname): 76 | f = open(fname, "wb") 77 | c = csv.writer(f) 78 | c.writerow(["Diff_MAP","OldBinName","NewBinName","Old File Version","New File Version","Modified","Company", "ChangedFunctionAddress"]) 79 | f.close() 80 | def addToLog(f_name,data): 81 | f = open(f_name, "ab") 82 | c = csv.writer(f) 83 | c.writerow(data) 84 | f.close() 85 | def IDBJobThread(old_pack,new_pack,new,old,file,log_name): 86 | #print '[+] Operating on',file 87 | fresh_list = [] 88 | 89 | diff_name = old_pack+'=>'+new_pack 90 | vm_ver_old = old_pack 91 | vm_ver_new = new_pack 92 | new_abs_path = new_pack+'\\'+file 93 | old_abs_path = old_pack+'\\'+file 94 | new_file_ver = new[file] 95 | old_file_ver = old[file] 96 | description = repr(getFileDescription(new_abs_path)).lower() 97 | 98 | if new[file] != old[file]: 99 | modified = 'Yes' 100 | else: 101 | modified = 'No' 102 | if modified == 'Yes' and new_file_ver != old_file_ver and 'vmware' in description and 'VMware Workstation\\' in new_abs_path and 'vmware-vmx-debug.exe' not in new_abs_path and 'vmware-vmx-stats.exe' not in new_abs_path: 103 | print '[+] Operating on',file 104 | fresh_list.append(diff_name) 105 | fresh_list.append(old_abs_path) 106 | fresh_list.append(new_abs_path) 107 | fresh_list.append(new_file_ver) 108 | fresh_list.append(old_file_ver) 109 | fresh_list.append(modified) 110 | fresh_list.append(description) 111 | # Try to get the diffing info. 112 | try: 113 | primary_BinExport = GenerateBinExport(old_abs_path) 114 | secondary_BinExport = GenerateBinExport(new_abs_path) 115 | 116 | Bindiff_file = GenerateBINDIFF(primary_BinExport,secondary_BinExport) 117 | changed_funk = ParseBinDiff(Bindiff_file) 118 | 119 | os.remove(primary_BinExport) # We only remove the old binexport file since it will not be required later 120 | #os.remove(secondary_BinExport) # We keep the secondary binary ; since it will be requried as primary later. 121 | os.remove(Bindiff_file) 122 | fresh_list.append(changed_funk) # No Error Hence we are writing all changed function addresses.. 123 | except Exception as e: 124 | fresh_list.append(str(e)) # Write the error in CSV file. 125 | addToLog(log_name,fresh_list) 126 | else: 127 | pass 128 | return True 129 | if __name__ == "__main__": 130 | for i in range(0,len(all_packages)): 131 | old_pack = all_packages[i] 132 | new_pack = all_packages[i+1] 133 | fname = old_pack+'-'+new_pack+'.csv' 134 | openLog(fname) 135 | print '*'*50 136 | print '[+] Old Workstation Version :',old_pack 137 | print '[+] New Workstation Version :',new_pack 138 | print '*'*50 139 | new = FindAllFilesBINFileInPackage(new_pack) 140 | old = FindAllFilesBINFileInPackage(old_pack) 141 | for file in new: 142 | if file in old: 143 | IDBJobThread(old_pack,new_pack,new,old,file,fname) 144 | #thread.start_new_thread(IDBJobThread, (old_pack,new_pack,new,old,file,fname,)) 145 | #sleep(1) 146 | else: 147 | pass --------------------------------------------------------------------------------