├── README.md ├── ioconfig.conf ├── iofuzz-prototype.py └── iofuzz.py /README.md: -------------------------------------------------------------------------------- 1 | iofuzz 2 | ====== 3 | 4 | A mutation based user mode (ring3) dumb in-memory Kernel Driver (IOCTL) Fuzzer/Logger. This script attach it self to any given user mode process and hooks DeviceIoControl!Kernel32 API call and try to log or fuzz all I/O Control code I/O buffer length that user process sends to any Kernel driver. 5 | -------------------------------------------------------------------------------- /ioconfig.conf: -------------------------------------------------------------------------------- 1 | [IOCTLFuzzerConfig] 2 | ProcessName: mcshield.exe 3 | FuzzInput: True 4 | FuzzInSize: False 5 | FuzzOutSize: True 6 | #if IoctlCodeToLog is set to * then all ioctl will be logged/fuzzed otherwise only given ioctl code will be logged/fuzzed. Example - IoctlCodeToLog = 0xdeadbeef 7 | IoctlCodeToLog = * 8 | -------------------------------------------------------------------------------- /iofuzz-prototype.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 3 | Author : Debasish Mandal 4 | Blog :http://www.debasish.in/ 5 | Twitter : https://twitter.com/debasishm89 6 | 7 | A mutation based user mode (ring3) dumb in-memory IOCTL Fuzzer/Logger. 8 | This script attach it self to any given process and hooks DeviceIoControl!Kernel32 API and 9 | try to log or fuzz all I/O Control code I/O Buffer pointer, I/O buffer length that 10 | process sends to any Kernel driver. 11 | 12 | 13 | ''' 14 | try: 15 | import utils 16 | except ImportError: 17 | print 'Could not import utils,Get it from https://github.com/OpenRCE/paimei' 18 | import pydbg 19 | from pydbg import * 20 | from pydbg.defines import * 21 | import random 22 | import math 23 | import ConfigParser 24 | import sys 25 | #Write these values in stack when size to be fuzzed 26 | fuzzed_size = ['\x00\x00\x00\x00','\xff\xff\xff\xff','\x00\x00\xff\xff','\xff\xff\x00\x00'] 27 | logfile_path = 'c:\\ioctllog.xml' 28 | 29 | def formatit(s): 30 | buff = '' 31 | for i in range(0,len(s)): 32 | if i%2 == 0: 33 | buff += '\\x' 34 | buff += s[i] 35 | return buff 36 | def fuzzit(buf): 37 | fuzzpercent = float(0.05) 38 | b = list(buf) 39 | numwrites=random.randrange(math.ceil((float(len(buf))) * fuzzpercent))+1 40 | for j in range(numwrites): 41 | rbyte = random.randrange(256) 42 | rn = random.randrange(len(buf)) 43 | b[rn] = '%c'%(rbyte) 44 | c=''.join(b) 45 | return c 46 | def startlog(): 47 | f = open(logfile_path,'w') 48 | f.write('') 49 | f.close() 50 | def writelog(data): 51 | f = open(logfile_path,'a') 52 | f.write(data) 53 | f.close() 54 | def sniff( dbg, args ): 55 | ''' 56 | BOOL WINAPI DeviceIoControl( 57 | _In_ HANDLE hDevice, 58 | _In_ DWORD dwIoControlCode, 59 | _In_opt_ LPVOID lpInBuffer, 60 | _In_ DWORD nInBufferSize, 61 | _Out_opt_ LPVOID lpOutBuffer, 62 | _In_ DWORD nOutBufferSize, 63 | _Out_opt_ LPDWORD lpBytesReturned, 64 | _Inout_opt_ LPOVERLAPPED lpOverlapped 65 | ); 66 | esp+4 -> 0 -> HANDLE hDevice 67 | esp+8 -> 1 -> DWORD dwIoControlCode 68 | esp+12-> 2 -> LPVOID lpInBuffer 69 | esp+16-> 3 -> DWORD nInBufferSize 70 | esp+20-> 4 -> LPVOID lpOutBuffer pointer 71 | esp+24-> 5 -> DWORD nOutBufferSize 72 | .. 73 | .. 74 | ''' 75 | log = "\n" 76 | log += '\n' + hex(args[1]) + '' 77 | print '[+] IOCTL Code : ',hex(args[1]) 78 | esp = dbg.context.Esp 79 | if fuzz_in_size: 80 | log+= '\n' + hex(args[3]) + '' 81 | print '[+] Actual In-Buffer Size :',hex(args[3]) 82 | fsz = random.choice(fuzzed_size) 83 | log+= '\n' + fsz + '' 84 | print '[+] Fuzzed In-Buffer Size :' , fsz 85 | dbg.write_process_memory( esp+0x10, fsz, 4) 86 | else: 87 | log+= '\n' + hex(args[3]) + '' 88 | print '[+] Logging Mode : In-Buffer Size : ',hex(args[3]) 89 | if fuzz_op_size: 90 | log+= '\n' + hex(args[5]) + '' 91 | print '[+] Actual Out-Buffer Size :',hex(args[5]) 92 | fsz = random.choice(fuzzed_size) 93 | log+= '\n' + fsz + '' 94 | print '[+] Fuzzed Out-Buffer Size :' , fsz 95 | dbg.write_process_memory( esp+0x18, fsz, 4) 96 | else: 97 | log+= '\n' + hex(args[5]) + '' 98 | print '[+] Logging Mode : Out-Buffer Size : ',hex(args[5]) 99 | input_buffer = dbg.read_process_memory(args[2], int(args[3])) 100 | if fuzz_input: 101 | fuzzed_data = fuzzit(input_buffer) 102 | log += '\n' + fuzzed_data.encode('hex') + '' 103 | log += '\n' + formatit(fuzzed_data.encode('hex')) + '' 104 | print '[+] Fuzzed Input-Buffer Data :',fuzzed_data.encode('hex') 105 | else: 106 | fuzzed_data = input_buffer 107 | log += '\n' + formatit(fuzzed_data.encode('hex')) + '' 108 | print '[+] Logging Mode : Input-Buffer Data :',fuzzed_data.encode('hex') 109 | try: 110 | dbg.write_process_memory( args[2], fuzzed_data, int(args[3])) #Writing fuzzed data into memory 111 | except Exception, e: 112 | log += '\n[+] Error : Cannot Write Fuzzed Data into memory!!' 113 | print '[+] Error : Cannot Write Fuzzed Data into memory!!' 114 | log += '' 115 | print '\n\n'+'*'*50 116 | if target_ioctl == "*": 117 | writelog(log) 118 | else: 119 | if args[1] == target_ioctl: 120 | writelog(log) 121 | else: 122 | print '[+] Skipping' 123 | return DBG_CONTINUE 124 | def parseconfig(): 125 | global proc_name,fuzz_input,fuzz_in_size,fuzz_op_size,target_ioctl 126 | config = ConfigParser.ConfigParser() 127 | try: 128 | config.read(sys.argv[1]) 129 | proc_name = config.get('IOCTLFuzzerConfig', 'ProcessName', 0) 130 | if_fuzz_input = config.get('IOCTLFuzzerConfig', 'FuzzInput', 0) 131 | if if_fuzz_input == "True": 132 | fuzz_input = True 133 | else: 134 | fuzz_input = False 135 | if_fuzz_in_size = config.get('IOCTLFuzzerConfig', 'FuzzInSize', 0) 136 | if if_fuzz_in_size == "True": 137 | fuzz_in_size = True 138 | else: 139 | fuzz_in_size = False 140 | if_fuzz_op_size = config.get('IOCTLFuzzerConfig', 'FuzzOutSize', 0) 141 | if if_fuzz_op_size == "True": 142 | fuzz_op_size = True 143 | else: 144 | fuzz_op_size = False 145 | try: 146 | target_ioctl = int(config.get('IOCTLFuzzerConfig', 'IoctlCodeToLog', 0),16) 147 | except Exception,e: 148 | target_ioctl = "*" 149 | print '\t[*]Process to Hook :',proc_name 150 | print '\t[*]Fuzz input buffer:',if_fuzz_input 151 | print '\t[*]Fuzz input size :',if_fuzz_input 152 | print '\t[*]Fuzz output buffer size :',if_fuzz_op_size 153 | print '\t[*] IOCTL to Log ',target_ioctl 154 | raw_input('[+] If above informations are corrects press enter to continue') 155 | except Exception,e: 156 | print '[+] Error Reading/Parsing Config file' 157 | print '[+] Usage :python iofuzz.py ioconfig.conf' 158 | exit() 159 | def main(): 160 | parseconfig() 161 | startlog() 162 | dbg = pydbg() 163 | hooks = utils.hook_container() 164 | for (pid,name) in dbg.enumerate_processes(): 165 | if name == proc_name: 166 | print '[+] Attaching to ',proc_name 167 | try: 168 | dbg.attach(pid) 169 | except Exception,e: 170 | print '[Error] Cannot Attach to process ',proc_name,pid 171 | exit() 172 | hook_address = dbg.func_resolve_debuggee("kernel32.dll","DeviceIoControl") 173 | hooks.add( dbg, hook_address, 8, sniff, None ) 174 | dbg.run() 175 | if __name__ == '__main__': 176 | main() 177 | -------------------------------------------------------------------------------- /iofuzz.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 3 | iofuzz.py 4 | Author : Debasish Mandal 5 | Blog : http://www.debasish.in/ 6 | 7 | This fuzzer attaches it self to given user mode process and hooks DeviceIoControl!Kernel32. 8 | After that,when DeviceIoControl is get called by the process it fuzzes the input/output buffer 9 | content etc inside memory and at the same time logs actual buffer and mutated buffer length / content 10 | in a log file. This log file is helpful when reproducing bugs triggered by this fuzzer. 11 | 12 | This tool can operates in three modes: 13 | 14 | 1. Sniffing Mode - Capture Phase 15 | 2. Passive Fuzzing Mode - In-memory Fuzzing Mode 16 | 3. Active Fuzzing Mode - Direct operation I/O to Target device driver. 17 | 18 | 19 | ''' 20 | 21 | import optparse 22 | import re 23 | import random 24 | import math 25 | from random import randrange,randint 26 | try: 27 | from ctypes import * 28 | from ctypes import wintypes 29 | except ImportError: 30 | '[Error] Could not import ctype' 31 | try: 32 | from win32api import GetLastError;(kernel32,ntdll) = (windll.kernel32,windll.ntdll) 33 | from win32con import NULL,FILE_SHARE_READ,FILE_SHARE_WRITE,OPEN_EXISTING 34 | from win32file import INVALID_HANDLE_VALUE 35 | except ImportError: 36 | print '[Error] Could not import pywin32.Get it from http://sourceforge.net/projects/pywin32/' 37 | try: 38 | import utils 39 | except ImportError: 40 | print '[Error]Could not import utils,Get it from https://github.com/OpenRCE/paimei' 41 | try: 42 | import pydbg 43 | from pydbg import * 44 | from pydbg.defines import * 45 | except ImportError: 46 | print '[Error] Could not import pydbg. Get it from https://github.com/OpenRCE/pydbg' 47 | from datetime import datetime 48 | import signal 49 | import sys 50 | 51 | 52 | class CMDOptions: 53 | ''' 54 | Although optparse has very nice help banner printing options, still I just wanted to customize the help banner little bit.Nothing else :) 55 | ''' 56 | def show_passive_help(self): 57 | print ''' 58 | When --mode="passive" (in-memory fuzzing mode) 59 | Mandatory switches: 60 | --process (ProcessName to attach and Its mandatory option) 61 | Optional Switches: 62 | --ioctl (Code to log / log all iocode) Ex. --ioctl=0xbadfood or by default it will fuzz all iocodes inside memory! 63 | ''' 64 | def show_active_help(self): 65 | print ''' 66 | When --mode="active" (Send IOCTL direct to target device given) 67 | Mandatory switches: 68 | --device (Target device name Its mandatory option) 69 | --inputlog (Input log collected after iofuzz sniffer, inputlog.txt and Its mandatory option) 70 | Example : --mode="active" --device="MyDevice" --inputlog="iofuzz_2014_03_23_22_47_10.log" --fuzzinlen --fuzzinbuff 71 | Optional Switches: 72 | --fuzzinbuff (Fuzz input buffer content False by default) 73 | --fuzzinlen (Fuzz input buffer length False by default) 74 | --fuzzoutlen (Fuzz input buffer length False by default) 75 | --sizelimit (Length size limit 100,200 etc etc default is 10K) 76 | --ioctl (Code to log / log all iocode) Ex. --ioctl=0xbadfood by default it will fuzz all iocodes 77 | ''' 78 | def show_sniff_help(self): 79 | 80 | print ''' 81 | When --mode="sniff" (silently log all device IO control request) 82 | Mandatory switches: 83 | --process (Process Name to attach with) Example : --mode="sniff" --process="notepad.exe" 84 | Optional Switches: 85 | --ioctl (Code to log / log all iocode) Ex. --ioctl=0xbadfood or --ioctl="*" 86 | ''' 87 | def cmd_option(self): 88 | ''' 89 | Parse Options 90 | ''' 91 | global mode,log_path,fuzzinbuff,fuzzinlen,fuzzoutlen,size_limit,process,device 92 | fuzzinbuff = False 93 | fuzzoutlen = False 94 | fuzzinlen = False 95 | size_limit = 10000 96 | global input_log_path,ioctl,run_time 97 | parser = optparse.OptionParser() 98 | parser.add_option('--mode', type="string", dest='mode') 99 | parser.add_option('--process', type="string", dest='process') 100 | parser.add_option('--device', type="string", dest='device') 101 | ### boolian ###### 102 | parser.add_option('--fuzzinbuff', dest='fuzzinbuff',default=False,action="store_true") 103 | parser.add_option('--fuzzinlen', dest='fuzzinlen',default=False,action="store_true") 104 | parser.add_option('--fuzzoutlen', dest='fuzzoutlen',default=False,action="store_true") 105 | #### helper options #### 106 | parser.add_option('--sizelimit', type="int", dest='size_limit') 107 | parser.add_option('--inputlog', type="string", dest='input_log_path') 108 | parser.add_option('--ioctlcode', type="int", dest='ioctl') 109 | (opts, args) = parser.parse_args() 110 | mode = opts.mode 111 | if mode == "passive": 112 | mandatories = ['process'] 113 | for m in mandatories: 114 | if not opts.__dict__[m]: 115 | print "[+] Error!! You have missed a mandatory option of mode : ", mode 116 | self.show_passive_help() 117 | exit(-1) 118 | if opts.fuzzinbuff: 119 | fuzzinbuff = True 120 | if opts.fuzzoutlen: 121 | fuzzoutlen = True 122 | if opts.fuzzinlen: 123 | fuzzinlen = True 124 | if opts.ioctl: 125 | ioctl = int(opts.ioctl) 126 | else: 127 | ioctl = "*" 128 | if opts.size_limit: 129 | size_limit = opts.size_limit 130 | process = opts.process 131 | print "Mode:",mode,"Process:",process,"Fuzz input buffer:",fuzzinbuff,"Fuzz output length:",fuzzoutlen,"Fuzz input length:",fuzzinlen,"Ioctl:",ioctl,"Max buffer length limit",size_limit 132 | raw_input('[+] Press Enter to continue..') 133 | elif mode == "active": 134 | mandatories = ['input_log_path','device'] 135 | for m in mandatories: 136 | if not opts.__dict__[m]: 137 | print "[+] Error!! You have missed a mandatory option of mode : ", mode 138 | self.show_active_help() 139 | exit(-1) 140 | if opts.fuzzinbuff: 141 | fuzzinbuff = True 142 | if opts.fuzzoutlen: 143 | fuzzoutlen = True 144 | if opts.fuzzinlen: 145 | fuzzinlen = True 146 | if opts.ioctl: 147 | ioctl = int(opts.ioctl) 148 | else: 149 | ioctl = "*" 150 | if opts.size_limit: 151 | size_limit = opts.size_limit 152 | device = opts.device 153 | input_log_path = opts.input_log_path 154 | #if opts.fuzzinlen and opts.fuzzinbuff: 155 | # print '[Error] Please choose any one of --fuzzinlen or --fuzzinbuff switch at a time' 156 | # exit() 157 | print "[+] Mode",mode,"Input Log Path:",input_log_path,"Device Name:",device,"Fuzz input buffer:",fuzzinbuff,"Fuzz output length:",fuzzoutlen,"Fuzz input length:",fuzzinlen,"Ioctl:",ioctl,"Max buffer length limit",size_limit 158 | raw_input('[+] Press Enter to continue..') 159 | elif mode == "sniff": 160 | mandatories = ['process'] 161 | for m in mandatories: 162 | if not opts.__dict__[m]: 163 | print "[+] Error!! You have missed a mandatory option Example \n$./iofuzz.py --mode " 164 | self.show_sniff_help() 165 | exit(-1) 166 | #log = opts.log 167 | process = opts.process 168 | if opts.ioctl: 169 | ioctl = int(opts.ioctl) 170 | else: 171 | ioctl = "*" 172 | print '[+] Mode:',mode,"Process Name:",process 173 | raw_input('[+] Press Enter to continue..') 174 | 175 | else: 176 | print '''[+] Invalid or no mode name given. \nExample $./iofuzz.py --mode 177 | passive => (In-memory fuzzing mode) 178 | active => (Send IOCTL direct to target device given) 179 | sniffer => (Silently log all device IO control request by hooking Kernel32!DeviceIoControl()) 180 | ''' 181 | exit() 182 | class HelperFunctions: 183 | def startlog(self,logfile_path): 184 | f = open(logfile_path,'w') 185 | f.write('') 186 | f.close() 187 | def exit_gracefully(self,signum, frame): 188 | signal.signal(signal.SIGINT, original_sigint) 189 | try: 190 | if raw_input("Really want to stop? (y/n)> ").lower().startswith('y'): 191 | sys.exit(1) 192 | except KeyboardInterrupt: 193 | hookobj.removehook() 194 | print("Ok ok, quitting") 195 | sys.exit(1) 196 | signal.signal(signal.SIGINT, exit_gracefully) 197 | def addtolog(self,ioctl,input_buffer,output_buffer_len,out_buff_address): 198 | log = '' 199 | log += '' + hex(ioctl) +'' 200 | log += ''+ input_buffer + '' 201 | log += '' + hex(output_buffer_len) +'' 202 | log += '' + hex(out_buff_address) + '' 203 | log += '' 204 | f = open(log_name,'a') 205 | f.write(log) 206 | f.close() 207 | 208 | class Fuzzing: 209 | ''' 210 | This class is responsible for all fuzzing operations.(generation and mutation) 211 | ''' 212 | #Generation Based fuzzing 213 | def get_long_buffer(self): 214 | total_cases = 2 215 | case = randrange(total_cases) 216 | if case == 0: 217 | #send back long strings 218 | chars = ['A','O','Z','!','@','#','$','^','*','+','\\','<','>','?','`','~','\"',"\\'",'%','%s','%d','%x','%u','%p','\xff','\x00'] 219 | return chars[randrange(0,26)]*randrange(0,5000) 220 | if case == 1: 221 | #send back dwords 222 | dw = ['\x00\x00\x00\x00','\xFF\xFF\xFF\xFF','\xFF\xFF\x00\x00','\x00\x00\xFF\xFF'] 223 | return (random.choice(dw))*randrange(0,10000) 224 | if case == 2: 225 | random_byte = random.randrange(256) 226 | return hex(random_byte)[2:].decode('hex')*randint(0,5000) 227 | ####################################################################### 228 | ''' 229 | This if for output buffer size and address ! 230 | ''' 231 | def get_long_len(self): 232 | return randrange(0x0000,0xFFFF) 233 | def get_address(self): 234 | ad_case = randrange(0,1) 235 | if ad_case == 0: 236 | return randrange(0x00000000,0x7FFFFFFF) 237 | if ad_case == 1: 238 | return randrange(0x80000000,0xFFFFFFFF) 239 | ############################################################## 240 | def dumbo(self,buf): 241 | ''' 242 | Dumbest fuzzer in the whole universe for sure!!! 243 | ''' 244 | if len(buf) == 0: 245 | return "" 246 | percent = float(0.05) 247 | b = list(buf) 248 | num2write=random.randrange(math.ceil((float(len(buf))) * percent))+1 249 | for j in range(num2write): 250 | random_byte = random.randrange(256) 251 | random_offset = random.randrange(len(buf)) 252 | b[random_offset] = '%c'%(random_byte) 253 | final=''.join(b) 254 | return final 255 | def dwordfuzz(self,buff): 256 | ''' 257 | Fuzz only DWORDs of given buffer!! 258 | ''' 259 | if len(buff) == 0: 260 | return "" 261 | dwords = ['\x00\x00\x00\x00','\xff\xff\xff\xff','\x00\x00\xff\xff','\xff\xff\x00\x00','\x00\x00\x01\x00'] 262 | dword_buff = [buff[i:i+4] for i in range(0, len(buff), 4)] 263 | rand_offset = randrange(len(dword_buff)) 264 | dword_buff[rand_offset] = dwords[randrange(0,len(dwords))] 265 | return ''.join(dword_buff) 266 | def fuzzit(self,buff): 267 | ''' 268 | It decides which of above defined fuzzer to be used depending on the choosen options!! 269 | ''' 270 | if fuzzinlen and mode != "passive": 271 | ''' 272 | Its sometime too risky to write long data inside memory. It may corrupt existing data structure. 273 | So for passive mode only mutation based approach will be used. 274 | ''' 275 | #Check if buffer length can be manipulated or not 276 | return self.get_long_buffer() 277 | else: 278 | #Length cannot be manipulated then use dumbo() or dwordfuzz() 279 | lot = randint(0,1) 280 | if lot == 0: 281 | return self.dwordfuzz(buff) 282 | if lot == 1: 283 | return self.dumbo(buff) 284 | 285 | class SendIoctl: 286 | ''' 287 | This Class is responsible for doing all direct I/O operations to target device. 288 | Thanks for levle his work saved lot of my time. 289 | https://github.com/levle/Kfuzz 290 | ''' 291 | def sendioctl(self,device,ioctl,input_buffer,output_buf_poi,output_len): 292 | f = open('lastio.log','w') 293 | DeviceIoControl = windll.ntdll.ZwDeviceIoControlFile 294 | input_size = len(input_buffer) 295 | handle = kernel32.CreateFileA("\\\\.\\"+device,FILE_SHARE_WRITE|FILE_SHARE_READ,0,None,OPEN_EXISTING,0,None) 296 | handleMap = kernel32.CreateFileMappingA(INVALID_HANDLE_VALUE,NULL,0x40,0,input_size,NULL) 297 | address = kernel32.MapViewOfFileEx(handleMap,0x2|0x4|0x8,0,0,input_size,NULL) 298 | kernel32.WriteProcessMemory(-1,address,input_buffer,input_size,byref(c_int(0))) 299 | print '[*] Device: '+str(device)+' Handle: '+hex(handle)+' IOCTL: '+hex(ioctl)+' Buffer(in) Poi: '+hex(address)+' Length: '+hex(input_size)+' Buffer(out): '+hex(output_buf_poi)+' Length: '+hex(output_len) 300 | log = ''+ device +''+ ioctl +''+ input_buffer +''+ output_buf_poi +''+ output_len +'' 301 | f.write(log) 302 | f.close() 303 | ret = DeviceIoControl(handle,NULL,NULL,NULL,byref(c_ulong()),ioctl,address,input_size,output_buf_poi,output_len) 304 | if not ret: 305 | raise IOError('DeviceIoControl() failed!') 306 | kernel32.CloseHandle(handle) 307 | kernel32.UnmapViewOfFile(address) 308 | class HooknSniff: 309 | ''' 310 | This class is responsible for actual Hook operation and it changes all DeviceIoControl params on the fly!! 311 | ''' 312 | def sniff( self,dbg, args ): 313 | ''' 314 | BOOL WINAPI DeviceIoControl( 315 | _In_ HANDLE hDevice, 316 | _In_ DWORD dwIoControlCode, 317 | _In_opt_ LPVOID lpInBuffer, 318 | _In_ DWORD nInBufferSize, 319 | _Out_opt_ LPVOID lpOutBuffer, 320 | _In_ DWORD nOutBufferSize, 321 | _Out_opt_ LPDWORD lpBytesReturned, 322 | _Inout_opt_ LPOVERLAPPED lpOverlapped 323 | ); 324 | esp+4 -> 0 -> HANDLE hDevice 325 | esp+8 -> 1 -> DWORD dwIoControlCode 326 | esp+C-> 2 -> LPVOID lpInBuffer 327 | esp+10-> 3 -> DWORD nInBufferSize 328 | esp+14-> 4 -> LPVOID lpOutBuffer pointer 329 | esp+18-> 5 -> DWORD nOutBufferSize 330 | .. 331 | .. 332 | ''' 333 | in_size = args[3] 334 | in_buff_poi = args[2] 335 | input_buffer = dbg.read_process_memory(in_buff_poi, int(in_size)) 336 | output_buffer_poi = args[4] 337 | io_code = args[1] 338 | out_size = args[5] 339 | esp = dbg.context.Esp 340 | print '[+] Intercepted IOCTL Code :',hex(io_code) 341 | if mode == "passive": 342 | if ioctl != "*": 343 | #operate on given ioctl 344 | if args[1] == ioctl: 345 | if fuzzinbuff: 346 | fuzzed = fuzz.fuzzit(input_buffer) 347 | length = len(fuzzed) 348 | a = '{0:08X}'.format(length) 349 | dbg.write_process_memory( esp+0x10, a.decode('hex'), 4) 350 | dbg.write_process_memory( args[2], fuzzed, len(fuzzed)) 351 | else: 352 | fuzzed = input_buffer 353 | long_length = fuzz.get_long_len() 354 | a = '{0:08X}'.format(long_length) 355 | dbg.write_process_memory( esp+0x18, a.decode('hex'), 4) 356 | rand_addr = addfuzz.get_address() 357 | b = '{0:08X}'.format(rand_addr) #convert int to string represent a 32bit Hex number 358 | raw_length = b.decode('hex') 359 | dbg.write_process_memory( esp+0x14, raw_length, 4) 360 | helper.addtolog(io_code,fuzzed,long_length,rand_addr) 361 | else: 362 | print '[+] Skipping IOCTL..',hex(args[1]) 363 | else: 364 | if fuzzinbuff: 365 | fuzzed = fuzz.fuzzit(input_buffer) 366 | length = len(fuzzed) 367 | a = '{0:08X}'.format(length) 368 | dbg.write_process_memory( esp+0x10, a.decode('hex')[::-1], 4) 369 | dbg.write_process_memory( args[2], fuzzed, len(fuzzed)) 370 | else: 371 | #Dont fuzz input buffer content 372 | fuzzed = input_buffer 373 | #Long out put size out put buffer len 374 | long_out_length = fuzz.get_long_len() 375 | a = '{0:08X}'.format(long_out_length) 376 | dbg.write_process_memory( esp+0x18,a.decode('hex')[::-1], 4) 377 | #Write an absurd address in output buffer address 378 | rand_addr = fuzz.get_address() 379 | a = '{0:08X}'.format(rand_addr) #convert int to string represent a 32bit Hex number 380 | raw_length = a.decode('hex') 381 | dbg.write_process_memory( esp+0x14, raw_length[::-1], 4) 382 | helper.addtolog(io_code,fuzzed,long_out_length,rand_addr) 383 | if mode == "sniff": 384 | #sniffing mode 385 | if ioctl == "*": 386 | helper.addtolog(io_code,input_buffer,out_size,output_buffer_poi) 387 | else: 388 | if args[1] == ioctl: 389 | #Log only this one 390 | helper.addtolog(io_code,input_buffer,out_size,output_buffer_poi) 391 | else: 392 | print '[+] Not logging',hex(args[1]) 393 | print '\n\n'+'*'*50 394 | return DBG_CONTINUE 395 | def removehook(dbg): 396 | #remove_hook_and deattach debugger 397 | print '[+] Rmeoving DeviceIoControlHook' 398 | hooks.remove( dbg, hook_address) 399 | dbg.detach() 400 | if __name__ == "__main__": 401 | cmd = CMDOptions() 402 | cmd.cmd_option() 403 | global helper 404 | helper = HelperFunctions() 405 | global fuzz 406 | fuzz = Fuzzing() 407 | global original_sigint 408 | original_sigint = signal.getsignal(signal.SIGINT) 409 | signal.signal(signal.SIGINT, helper.exit_gracefully) 410 | global log_name 411 | log_name = 'iofuzz_'+datetime.now().strftime("%Y_%m_%d_%H_%M_%S")+".log" 412 | helper.startlog(log_name) 413 | if mode == "passive" or mode == "sniff": 414 | global dbg 415 | dbg = pydbg() 416 | global hooks 417 | hooks = utils.hook_container() 418 | for (pid,name) in dbg.enumerate_processes(): 419 | if name == process: 420 | print '[+] Attaching to ',process 421 | try: 422 | dbg.attach(pid) 423 | except Exception,e: 424 | print '[Error] Cannot Attach to process ',process,pid 425 | exit() 426 | global hook_address 427 | hook_address = dbg.func_resolve_debuggee("kernel32.dll","DeviceIoControl") 428 | hookobj = HooknSniff() 429 | hooks.add( dbg, hook_address, 8, hookobj.sniff, None ) 430 | dbg.run() 431 | else: 432 | ioobj = SendIoctl() 433 | print '[+] Reading iocltl log file..',input_log_path 434 | f = open(input_log_path,'rb') 435 | xml_data = f.read() 436 | f.close() 437 | match = re.findall('(.*?)',xml_data) 438 | if not len(match): 439 | print '[Error] Make sure provided log is correctly formatted!!' 440 | exit() 441 | while 1: 442 | io = random.choice(match) 443 | iocode = re.findall('(.*?)',io) 444 | in_buff_data = re.findall('(.*?)',io) 445 | if ioctl == "*": 446 | if fuzzinbuff: 447 | fuzzed_data = fuzz.fuzzit(in_buff_data[0]) 448 | else: 449 | fuzzed_data = in_buff_data[0] 450 | out_address = fuzz.get_address() 451 | out_len = fuzz.get_long_len() 452 | ioobj.sendioctl(device,int(iocode[0],16),fuzzed_data,out_address,out_len) 453 | else: 454 | if int(iocode[0],16) == ioctl: 455 | if fuzzinbuff: 456 | fuzzed_data = fuzz.fuzzit(in_buff_data[0]) 457 | else: 458 | fuzzed_data = in_buff_data[0] 459 | out_address = fuzz.get_address() 460 | out_len = fuzz.get_long_len() 461 | ioobj.sendioctl(device,int(iocode[0],16),fuzzed_data,out_address,out_len) 462 | else: 463 | pass 464 | --------------------------------------------------------------------------------