├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README ├── adbfuzz.py ├── adbfuzzconfig.py ├── detectors.py.stub ├── helloworld.cfg ├── helloworld.html ├── helloworld.js ├── logfilter.py ├── mail.py ├── minidump.py ├── misc └── prefs.js ├── tools ├── compose.py └── test.py ├── triage.py └── websocklog.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Python compiled files 2 | *.py[cod] 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Community Participation Guidelines 2 | 3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines. 4 | For more details, please read the 5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). 6 | 7 | ## How to Report 8 | For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page. 9 | 10 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Version: MPL 2.0 2 | 3 | This Source Code Form is subject to the terms of the Mozilla Public 4 | License, v. 2.0. If a copy of the MPL was not distributed with this 5 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | The Original Code is ADBFuzz. 8 | 9 | The Initial Developer of the Original Code is Christian Holler (decoder). 10 | 11 | Contributors: 12 | Christian Holler (Original Developer) 13 | Jesse Ruderman 14 | Gary Kwong 15 | 16 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | == Quick Start Guide == 2 | 3 | === Important Notice === 4 | 5 | This software is a prototype, it's not heavily tested and it was 6 | developed in a specific environment. Don't expect everything to 7 | work out of the box. Be prepared to solve problems related to your 8 | environment, configuration and defects in this code. If you hit a 9 | problem you cannot solve, let us know. 10 | 11 | You can find us on IRC at irc.mozilla.org, channel #security. 12 | 13 | Or you can write me an email to decoder@mozilla.com. 14 | 15 | Furthermore, if you make changes to this code, e.g. bugfixes or 16 | modifications that others would benefit from as well, please be 17 | fair and share them :) 18 | 19 | 20 | === Requirements === 21 | 22 | In order to use this software you need: 23 | 24 | * The mozdevice module: 25 | Tested with my fork at 26 | https://github.com/choller/mozbase/tree/master/mozdevice 27 | but changes are regularly merged to main. 28 | 29 | * A working Android Development environment (in particular ADB) 30 | 31 | * A rooted Android device with Fennec (Firefox Mobile) with 32 | crash reporter enabled. 33 | OR 34 | * A non-rooted Android device with your own debuggable Firefox 35 | Mobile build (see end of this doc) and crash reporter enabled. 36 | 37 | * A network connection between your host machine and the Android 38 | device, e.g. a common LAN/WLAN. 39 | 40 | * A Firefox profile on the device with settings as shown in 41 | the misc/prefs.js file. You can simply copy this file to 42 | the profile directory while Firefox is not running. 43 | (DON'T use your productive profile for this!) 44 | 45 | * The em-websocket-proxy script (gem install em-websocket-proxy). 46 | 47 | 48 | === Configuring the Sample Fuzzer === 49 | 50 | Open the file helloworld.cfg, adjust localHost to match your host's LAN 51 | IP address. If you are attempting to use ADB over TCP/IP, rather than over 52 | a USB connection, also set the remoteHost variable appropriately. 53 | 54 | 55 | === Starting the Sample Fuzzer === 56 | 57 | Start the fuzzer with the following command: 58 | 59 | python adbfuzz.py helloworld.cfg run 60 | 61 | You'll see all sorts of debug messages, but if everything goes right, you 62 | should see Fennec popup on the device, trying to contact the host to load 63 | the fuzzing code. 64 | 65 | The sample fuzzer included is just a little demo that makes a pink square 66 | div bounce around using random CSS transformations. It's unlikely that this 67 | alone will find bugs, but I think it's a good demonstration of what you can 68 | do. 69 | 70 | === Reproducing crashes === 71 | 72 | The sample fuzzer sends all commands it executes using websockets. Once the 73 | harness detects a crash, it will copy the logfiles (websocket+syslog) and store 74 | them together with the crash dump. You need to extract the information from the 75 | log and replace the "start();" call at the end of the fuzzer file with those 76 | commands to replay them. 77 | 78 | 79 | === Advanced: Creating a debuggable Firefox build for use with non-rooted devices === 80 | 81 | The harness supports running on non-rooted devices, given that the "run-as" functionality 82 | is working. Using "run-as" requires the installed target package to be marked in a special 83 | way ("debuggable"), because it allows other apps to access the data of that application, 84 | which would be a security problem. To build your own Fennec debuggable package, perform 85 | the following steps: 86 | 87 | 1. Get a working build environment for Fennec: 88 | https://wiki.mozilla.org/Mobile/Fennec/Android 89 | 90 | 2. Modify the file mobile/android/base/AndroidManifest.xml.in: 91 | In that file, search for "debuggable", you will find a conditional where it's set to true 92 | or false based on MOZILLA_OFFICIAL. Make sure it's always true. 93 | 94 | 3. Use the following .mozconfig to build (make -f client.mk && make -C objdir-droid package): 95 | 96 | # Add the correct paths here 97 | ac_add_options --with-android-ndk="/home/build/NVPACK/android-ndk" 98 | ac_add_options --with-android-sdk="/home/build/NVPACK/android-sdk/platforms/android-13" 99 | ac_add_options --with-android-version=5 100 | ac_add_options --with-android-tools="/home/build/NVPACK/android-sdk/tools" 101 | # android options 102 | ac_add_options --enable-application=mobile/android 103 | ac_add_options --target=arm-linux-androideabi 104 | ac_add_options --with-endian=little 105 | ac_add_options --with-ccache 106 | ac_add_options --enable-tests 107 | ac_add_options --disable-elf-hack 108 | ac_add_options --enable-debug-symbols 109 | export MOZ_OLD_LINKER=1 110 | # 32 bit 111 | ac_add_options --host=i386-unknown-linux 112 | HOST_CC="gcc -m32" 113 | HOST_CXX="g++ -m32" 114 | mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/objdir-droid 115 | mk_add_options MOZ_MAKE_FLAGS="-j8" 116 | ac_add_options --enable-optimize 117 | ac_add_options --enable-debug 118 | export MOZILLA_OFFICIAL=1 119 | 120 | 4. Install the resulting package in objdir-droid/dist/ to your device. 121 | 122 | 5. Verify it's running, using "adb shell run-as org.mozilla.fennec_yourusername ls". 123 | 124 | Unfortunately, some (older) Android versions ship a broken version of the "run-as" tool, 125 | and if that is the case with your device, then there is no other way than rooting it, sorry :( 126 | -------------------------------------------------------------------------------- /adbfuzz.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # ***** BEGIN LICENSE BLOCK ***** 3 | # Version: MPL 2.0 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | # 9 | # The Original Code is ADBFuzz. 10 | # 11 | # The Initial Developer of the Original Code is Christian Holler (decoder). 12 | # 13 | # Contributors: 14 | # Christian Holler (Original Developer) 15 | # 16 | # ***** END LICENSE BLOCK ***** 17 | 18 | import subprocess 19 | import time 20 | import shutil 21 | import os 22 | import signal 23 | import sys 24 | import traceback 25 | import random 26 | import uuid 27 | from ConfigParser import SafeConfigParser 28 | 29 | from mozdevice import DeviceManagerADB 30 | 31 | from adbfuzzconfig import ADBFuzzConfig 32 | from triage import Triager 33 | from minidump import Minidump 34 | from logfilter import LogFilter 35 | 36 | def usage(): 37 | print "" 38 | print "Usage: " + sys.argv[0] + " cfgFile cmd params" 39 | print " Supported commands:" 40 | print " run - Run the fuzzer until manually aborted or until" 41 | print " reaching the specified number of iterations" 42 | print " param[0]: number of iterations (optional)" 43 | print " deploy - Deploy the given Firefox package and prefs" 44 | print " param[0]: path to package file" 45 | print " param[1]: path to prefs file" 46 | print " showdump - Show the (symbolized) trace for a given dump" 47 | print " param[0]: path to dump file" 48 | print " param[1]: symbol search path" 49 | print " reproduce" 50 | print " param[0]: file to test" 51 | print " param[1]: run timeout" 52 | print " param[2]: crash type (crash or abort)" 53 | print "" 54 | def main(): 55 | if (len(sys.argv) > 2): 56 | cfgFile = sys.argv.pop(1) 57 | fuzzInst = ADBFuzz(cfgFile) 58 | else: 59 | print "Missing configuration file!" 60 | usage() 61 | exit(1) 62 | 63 | cmd = sys.argv.pop(1) 64 | if (cmd == "showdump"): 65 | print "Obtaining symbolized trace..." 66 | dumpFile = sys.argv[1] 67 | libSearchPath = sys.argv[2] 68 | minidump = Minidump(dumpFile, libSearchPath) 69 | symbolTrace = minidump.getSymbolizedCrashTrace() 70 | print "" 71 | for frame in symbolTrace: 72 | print "#" + frame[0] + "\t" + frame[1] + " at " + frame[2] 73 | elif (cmd == "reproduce"): 74 | fuzzInst.config.fuzzerFile = sys.argv[1] 75 | fuzzInst.config.runTimeout = int(sys.argv[2]) 76 | 77 | isCrash = True 78 | 79 | if sys.argv[3] == 'crash': 80 | isCrash = True 81 | elif sys.argv[3] == 'abort': 82 | isCrash = False 83 | else: 84 | raise Exception("Unknown crash type " + sys.argv[3] + " specified!") 85 | 86 | if fuzzInst.testCrash(isCrash): 87 | exit(0) 88 | exit(1) 89 | elif (cmd == "run"): 90 | fuzzInst.remoteInit() 91 | if (len(sys.argv) > 2): 92 | fuzzInst.loopFuzz(sys.argv[1]) 93 | else: 94 | fuzzInst.loopFuzz() 95 | elif (cmd == "deploy"): 96 | fuzzInst.deploy(sys.argv[1], sys.argv[2]) 97 | elif (cmd == "reset"): 98 | fuzzInst.reset(sys.argv[1]) 99 | 100 | class ADBFuzz: 101 | 102 | def __init__(self, cfgFile): 103 | self.config = ADBFuzzConfig(cfgFile) 104 | 105 | self.HTTPProcess = None 106 | self.logProcesses = [] 107 | self.logThreads = [] 108 | self.remoteInitialized = None 109 | 110 | self.triager = Triager(self.config) 111 | 112 | # Seed RNG with localtime 113 | random.seed() 114 | 115 | def deploy(self, packageFile, prefFile): 116 | self.dm = DeviceManagerADB(self.config.remoteAddr, 5555) 117 | 118 | # Install a signal handler that shuts down our external programs on SIGINT 119 | signal.signal(signal.SIGINT, self.signal_handler) 120 | 121 | self.dm.updateApp(packageFile) 122 | 123 | # Standard init stuff 124 | self.appName = self.dm.packageName 125 | self.appRoot = self.dm.getAppRoot(self.appName) 126 | self.profileBase = self.appRoot + "/files/mozilla" 127 | 128 | # Ensure no Fennec instance is running 129 | self.stopFennec() 130 | 131 | # Start Fennec, so a profile is created if this is the first install 132 | self.startFennec(blank=True) 133 | 134 | # Grant some time to create profile 135 | time.sleep(self.config.runTimeout * 2) 136 | 137 | # Stop Fennec again 138 | self.stopFennec() 139 | 140 | # Now try to get the profile(s) 141 | self.profiles = self.getProfiles() 142 | 143 | if (len(self.profiles) == 0): 144 | print "Failed to detect any valid profile, aborting..." 145 | return 1 146 | 147 | self.defaultProfile = self.profiles[0] 148 | 149 | if (len(self.profiles) > 1): 150 | print "Multiple profiles detected, using the first: " + self.defaultProfile 151 | 152 | # Push prefs.js to profile 153 | self.dm.pushFile(prefFile, self.profileBase + "/" + self.defaultProfile + "/prefs.js") 154 | 155 | # Try to install addon if requested by configuration 156 | if self.config.addon != None: 157 | self.ensureExtensionInstalled(self.config.addon) 158 | 159 | print "Successfully deployed package." 160 | 161 | def reset(self, prefFile): 162 | self.dm = DeviceManagerADB(self.config.remoteAddr, 5555) 163 | 164 | # Install a signal handler that shuts down our external programs on SIGINT 165 | signal.signal(signal.SIGINT, self.signal_handler) 166 | 167 | # Standard init stuff 168 | self.appName = self.dm.packageName 169 | self.appRoot = self.dm.getAppRoot(self.appName) 170 | self.profileBase = self.appRoot + "/files/mozilla" 171 | 172 | # Ensure no Fennec instance is running 173 | self.stopFennec() 174 | 175 | # Now try to get the old profile(s) 176 | self.profiles = self.getProfiles() 177 | 178 | for profile in self.profiles: 179 | self.dm.removeDir(self.profileBase + "/" + profile) 180 | 181 | self.dm.removeFile(self.profileBase + "/profiles.ini") 182 | 183 | # Start Fennec, so a new profile is created 184 | self.startFennec(blank=True) 185 | 186 | # Grant some time to create profile 187 | time.sleep(self.config.runTimeout * 2) 188 | 189 | # Stop Fennec again 190 | self.stopFennec() 191 | 192 | # Now try to get the profile(s) again 193 | self.profiles = self.getProfiles() 194 | 195 | if (len(self.profiles) == 0): 196 | print "Failed to detect any valid profile, aborting..." 197 | return 1 198 | 199 | self.defaultProfile = self.profiles[0] 200 | 201 | if (len(self.profiles) > 1): 202 | print "Multiple profiles detected, using the first: " + self.defaultProfile 203 | 204 | # Push prefs.js to profile 205 | self.dm.pushFile(prefFile, self.profileBase + "/" + self.defaultProfile + "/prefs.js") 206 | 207 | # Try to install addon if requested by configuration 208 | if self.config.addon != None: 209 | self.ensureExtensionInstalled(self.config.addon) 210 | 211 | print "Successfully resetted profile." 212 | 213 | def remoteInit(self): 214 | if (self.remoteInitialized != None): 215 | return 216 | 217 | self.dm = DeviceManagerADB(self.config.remoteAddr, 5555) 218 | self.appName = self.dm.packageName 219 | self.appRoot = self.dm.getAppRoot(self.appName) 220 | self.profileBase = self.appRoot + "/files/mozilla" 221 | self.profiles = self.getProfiles() 222 | 223 | # Install a signal handler that shuts down our external programs on SIGINT 224 | signal.signal(signal.SIGINT, self.signal_handler) 225 | 226 | if (len(self.profiles) == 0): 227 | print "Failed to detect any valid profile, aborting..." 228 | return 1 229 | 230 | self.defaultProfile = self.profiles[0] 231 | 232 | if (len(self.profiles) > 1): 233 | print "Multiple profiles detected, using the first: " + self.defaultProfile 234 | 235 | 236 | # Workaround for bug 754575. Avoid using DeviceManagerADB's "removeDir" because 237 | # that calls "rm" on every single entry which takes a lot of additional time. 238 | print "Purging possible cache leftover directories..." 239 | self.dm.runCmd(['shell', 'rm', '-r', self.profileBase + "/" + self.defaultProfile + "/Cache.Trash*"]).communicate() 240 | 241 | self.remoteInitialized = True 242 | 243 | def signal_handler(self, signal, frame): 244 | self.cleanupProcesses() 245 | sys.exit(0) 246 | 247 | def cleanupProcesses(self): 248 | self.stopFennec() 249 | if (self.HTTPProcess != None): 250 | try: 251 | self.HTTPProcess.terminate() 252 | except: 253 | pass 254 | if (self.logProcesses != None): 255 | try: 256 | self.stopLoggers() 257 | except: 258 | pass 259 | 260 | def loopFuzz(self, maxIterations=None): 261 | try: 262 | iterations = 0 263 | while (maxIterations == None or maxIterations >= iterations): 264 | self.runFuzzer() 265 | iterations += 1 266 | except: 267 | self.cleanupProcesses() 268 | raise 269 | 270 | def runFuzzer(self): 271 | self.remoteInit() 272 | 273 | # Ensure Fennec isn't running 274 | if self.isFennecRunning(): 275 | self.stopFennec() 276 | 277 | # Clean all existing minidumps 278 | if not self.clearMinidumps(): 279 | raise Exception("Failed to clean existing minidumps") 280 | 281 | # Start our HTTP server for serving the fuzzer code 282 | self.HTTPProcess = self.startHTTPServer() 283 | 284 | # Start all loggers 285 | self.startLoggers() 286 | 287 | # Start Fennec 288 | self.startFennec() 289 | 290 | # Even though the program is already running, we should grant it 291 | # some extra time to load the fuzzer source and start running, 292 | # so it isn't directly diagnosed as hanging 293 | time.sleep(10); 294 | 295 | logSize = 0 296 | hangDetected = False 297 | forceRestart = False 298 | while(self.isFennecRunning() and not self.checkLoggingThreads()): 299 | time.sleep(self.config.runTimeout) 300 | 301 | if not os.path.exists(self.logFile): 302 | raise Exception("Logfile not present. If you are using websockets, this could indicate a network problem.") 303 | 304 | # Poor man's hang detection. Yes, this is a bad 305 | # idea, just for the sake of proof-of-concept 306 | newLogSize = os.path.getsize(self.logFile) 307 | if (logSize == newLogSize): 308 | hangDetected = True 309 | break 310 | else: 311 | logSize = newLogSize 312 | if newLogSize > self.config.maxLogSize: 313 | forceRestart = True 314 | break 315 | 316 | if hangDetected or forceRestart: 317 | self.stopFennec() 318 | self.stopLoggers() 319 | print "Hang detected or running too long, restarting..." 320 | else: 321 | try: 322 | # Fennec died or a logger found something 323 | checkCrashDump = True 324 | crashUUID = None 325 | minidump = None 326 | 327 | # If Fennec is still running, stop it now 328 | if self.isFennecRunning(): 329 | checkCrashDump = False 330 | self.stopFennec() 331 | 332 | # Terminate our logging processes first 333 | self.stopLoggers() 334 | 335 | if checkCrashDump: 336 | dumps = self.getMinidumps() 337 | if (len(dumps) > 1): 338 | raise Exception("Multiple dumps detected!") 339 | 340 | if (len(dumps) < 1): 341 | raise Exception("No crash dump detected!") 342 | 343 | if not self.fetchMinidump(dumps[0]): 344 | raise Exception("Failed to fetch minidump with UUID " + dumps[0]) 345 | 346 | crashUUID = dumps[0] 347 | 348 | # Copy logfiles 349 | shutil.copy2(self.syslogFile, dumps[0] + ".syslog") 350 | shutil.copy2(self.logFile, dumps[0] + ".log") 351 | 352 | minidump = Minidump(dumps[0] + ".dmp", self.config.libDir) 353 | else: 354 | # We need to generate an arbitrary ID here 355 | crashUUID = str(uuid.uuid4()) 356 | 357 | # Copy logfiles 358 | shutil.copy2(self.syslogFile, crashUUID + ".syslog") 359 | shutil.copy2(self.logFile, crashUUID + ".log") 360 | 361 | print "Crash detected. Reproduction logfile stored at: " + crashUUID + ".log" 362 | if checkCrashDump: 363 | crashTrace = minidump.getCrashTrace() 364 | crashType = minidump.getCrashType() 365 | print "Crash type: " + crashType 366 | print "Crash backtrace:" 367 | print "" 368 | print crashTrace 369 | else: 370 | print "Crash type: Abnormal behavior (e.g. Assertion violation)" 371 | 372 | self.triager.process(crashUUID, minidump, crashUUID + ".syslog", crashUUID + ".log") 373 | except Exception, e: 374 | print "Error during crash processing: " 375 | print traceback.format_exc() 376 | 377 | self.HTTPProcess.terminate() 378 | return 379 | 380 | def testCrash(self, isCrash): 381 | self.remoteInit() 382 | 383 | # Ensure Fennec isn't running 384 | if self.isFennecRunning(): 385 | self.stopFennec() 386 | 387 | # Clean all existing minidumps 388 | if not self.clearMinidumps(): 389 | raise Exception("Failed to clean existing minidumps") 390 | 391 | # Start our HTTP server for serving the fuzzer code 392 | self.HTTPProcess = self.startHTTPServer() 393 | 394 | # Start a logcat instance that goes to stdout for progress monitoring 395 | logProcess = self.startNewDeviceLog(toStdout=True) 396 | 397 | # Start Fennec 398 | self.startFennec() 399 | 400 | startTime = time.time() 401 | 402 | while(self.isFennecRunning() and not self.checkLoggingThreads()): 403 | time.sleep(1) 404 | if ((time.time() - startTime) > self.config.runTimeout): 405 | self.stopFennec() 406 | self.HTTPProcess.terminate() 407 | self.stopLoggers() 408 | print "[TIMEOUT]" 409 | return False 410 | 411 | abortedByLogThread = False 412 | if self.isFennecRunning(): 413 | abortedByLogThread = True 414 | self.stopFennec() 415 | 416 | self.HTTPProcess.terminate() 417 | self.stopLoggers() 418 | 419 | # Fennec died, check for crashdumps 420 | dumps = self.getMinidumps() 421 | if (len(dumps) > 0): 422 | if isCrash: 423 | print "[Crash reproduced successfully]" 424 | return True 425 | else: 426 | print "[Crashed while testing an abort]" 427 | return False 428 | elif abortedByLogThread: 429 | if isCrash: 430 | print "[Aborted while testing a crash]" 431 | return False 432 | else: 433 | print "[Abort reproduced successfully]" 434 | return True 435 | else: 436 | # Fennec exited, but no crash/abort 437 | print "[Exit without Crash]" 438 | return False 439 | 440 | def getProfiles(self): 441 | profiles = [] 442 | 443 | candidates = self.dm.listFiles(self.profileBase) 444 | for candidate in candidates: 445 | if self.dm.dirExists(self.profileBase + "/" + candidate + "/minidumps"): 446 | profiles.append(candidate) 447 | 448 | return profiles 449 | 450 | def getMinidumps(self): 451 | dumps = self.dm.listFiles(self.profileBase + "/" + self.defaultProfile + "/minidumps") 452 | dumpIDs = [] 453 | 454 | for dump in dumps: 455 | if dump.find('.dmp') > -1: 456 | dumpIDs.append(dump.replace('.dmp','')) 457 | 458 | return dumpIDs 459 | 460 | def fetchMinidump(self, dumpId): 461 | dumpPath = self.profileBase + "/" + self.defaultProfile + "/minidumps/" + dumpId + ".dmp" 462 | print dumpPath 463 | if (self.dm.getFile(dumpPath, dumpId + ".dmp") != None): 464 | return True 465 | 466 | return False; 467 | 468 | def clearMinidump(self, dumpId): 469 | dumpPath = self.profileBase + "/" + self.defaultProfile + "/minidumps/" + dumpId + ".dmp" 470 | extraPath = self.profileBase + "/" + self.defaultProfile + "/minidumps/" + dumpId + ".extra" 471 | 472 | if (self.dm.removeFile(dumpPath) != None and self.dm.removeFile(extraPath) != None): 473 | return True 474 | 475 | return False; 476 | 477 | def clearMinidumps(self): 478 | dumps = self.getMinidumps() 479 | for dump in dumps: 480 | if not self.clearMinidump(dump): 481 | return False 482 | 483 | return True 484 | 485 | def startLoggers(self): 486 | if self.config.useWebSockets: 487 | # This method starts itself multiple processes (proxy included) 488 | self.startNewWebSocketLog() 489 | self.startNewDeviceLog() 490 | 491 | def stopLoggers(self): 492 | # Terminate our logging processes 493 | while (len(self.logProcesses) > 0): 494 | self.logProcesses.pop().terminate() 495 | 496 | while (len(self.logThreads) > 0): 497 | logThread = self.logThreads.pop() 498 | logThread.terminate() 499 | logThread.join() 500 | 501 | def checkLoggingThreads(self): 502 | for logThread in self.logThreads: 503 | # Check if the thread aborted but did not hit EOF. 504 | # That means we hit something interesting. 505 | if not logThread.isAlive() and not logThread.eof: 506 | return True 507 | 508 | return False 509 | 510 | def startNewDeviceLog(self, toStdout=False): 511 | # Clear the log first 512 | subprocess.check_call(["adb", "logcat", "-c"]) 513 | 514 | logCmd = ["adb", "logcat", "-s", "Gecko:v", "GeckoDump:v", "GeckoConsole:v", "MOZ_Assert:v"] 515 | 516 | if toStdout: 517 | logFile = None 518 | else: 519 | # Logfile 520 | self.syslogFile = 'device.log' 521 | logFile = self.syslogFile 522 | 523 | # We start the logProcess here so we can terminate it before joining the thread 524 | logProcess = subprocess.Popen(logCmd, shell=False, stdout=subprocess.PIPE, stderr=None) 525 | 526 | # Start logging thread 527 | logThread = LogFilter(self.config, self.triager, logProcess, logFile) 528 | logThread.start() 529 | 530 | self.logThreads.append(logThread) 531 | self.logProcesses.append(logProcess) 532 | 533 | def startNewWebSocketLog(self): 534 | self.logFile = 'websock.log' 535 | logProcess = subprocess.Popen(["em-websocket-proxy", "-p", self.config.localListenPort, "-q", self.config.localWebSocketPort, "-r", "localhost"]) 536 | self.logProcesses.append(logProcess) 537 | proxyProcess = subprocess.Popen(["python", "websocklog.py", "localhost", self.config.localWebSocketPort]) 538 | self.logProcesses.append(proxyProcess) 539 | 540 | def startHTTPServer(self): 541 | HTTPProcess = subprocess.Popen(["python", "-m", "SimpleHTTPServer", self.config.localPort ]) 542 | return HTTPProcess 543 | 544 | def startFennec(self, blank=False): 545 | env = {} 546 | env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' 547 | env['MOZ_CRASHREPORTER_SHUTDOWN'] = '1' 548 | 549 | if blank: 550 | destURL = "about:blank" 551 | else: 552 | destURL = "http://" + self.config.localAddr + ":" + self.config.localPort + "/" + self.getSeededFuzzerFile() 553 | 554 | self.dm.launchProcess([self.appName, destURL], None, None, env) 555 | 556 | def stopFennec(self): 557 | ret = self.dm.killProcess(self.appName, True) 558 | 559 | if self.isFennecRunning(): 560 | # Try sleeping first and give the process time to react 561 | time.sleep(5) 562 | if self.isFennecRunning(): 563 | # If the process doesn't terminate, try SIGKILL 564 | print "Process did not react to SIGTERM, trying SIGKILL" 565 | return self.dm.killProcess(self.appName, True) 566 | 567 | return ret 568 | 569 | def isFennecRunning(self): 570 | procList = self.dm.getProcessList() 571 | for proc in procList: 572 | if (proc[1] == self.appName): 573 | return True 574 | 575 | return False 576 | 577 | def ensureExtensionInstalled(self, extensionFile): 578 | extDir = self.profileBase + "/" + self.defaultProfile + "/extensions" 579 | extensions = self.dm.listFiles(extDir) 580 | extensionFileName = os.path.basename(extensionFile) 581 | 582 | for extension in extensions: 583 | # Check if extension is already installed 584 | if extension == extensionFileName: 585 | return 586 | 587 | # Ensure the directory exists (has to be created in a new profile) 588 | self.dm.mkDir(extDir) 589 | 590 | extDest = extDir + '/' + extensionFileName 591 | 592 | if not self.dm.pushFile(extensionFile, extDest): 593 | raise Exception("Failed to install requested extension.") 594 | 595 | def getSeededFuzzerFile(self): 596 | fuzzerFile = self.config.fuzzerFile 597 | fuzzerSeed = random.randint(0,2**32-1) 598 | return fuzzerFile.replace('#SEED#', str(fuzzerSeed)) 599 | 600 | if __name__ == "__main__": 601 | main() 602 | -------------------------------------------------------------------------------- /adbfuzzconfig.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # ***** BEGIN LICENSE BLOCK ***** 3 | # Version: MPL 2.0 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | # 9 | # The Original Code is ADBFuzz. 10 | # 11 | # The Initial Developer of the Original Code is Christian Holler (decoder). 12 | # 13 | # Contributors: 14 | # Christian Holler (Original Developer) 15 | # 16 | # ***** END LICENSE BLOCK ***** 17 | 18 | from ConfigParser import SafeConfigParser 19 | 20 | class ADBFuzzConfig: 21 | 22 | def __init__(self, cfgFile): 23 | cfgDefaults = {} 24 | cfgDefaults['id'] = None 25 | cfgDefaults['remoteHost'] = None 26 | cfgDefaults['localPort'] = '8088' 27 | cfgDefaults['useWebSockets'] = False 28 | cfgDefaults['localWebSocketPort'] = '8089' 29 | cfgDefaults['localListenPort'] = '8090' 30 | cfgDefaults['localPortOffset'] = '0' 31 | cfgDefaults['libDir'] = None 32 | cfgDefaults['knownPath'] = None 33 | cfgDefaults['debug'] = str(False) 34 | cfgDefaults['useMail'] = str(False) 35 | cfgDefaults['runTimeout'] = str(5) 36 | cfgDefaults['maxLogSize'] = str(1024*1024) # Default to 1 MB maximum log 37 | cfgDefaults['addon'] = None 38 | 39 | self.cfg = SafeConfigParser(cfgDefaults) 40 | if (len(self.cfg.read(cfgFile)) == 0): 41 | raise Exception("Unable to read configuration file: " + cfgFile) 42 | 43 | self.id = self.cfg.get('main', 'id') 44 | self.fuzzerFile = self.cfg.get('main', 'fuzzer') 45 | self.runTimeout = self.cfg.getint('main', 'runTimeout') 46 | self.maxLogSize = self.cfg.getint('main', 'maxLogSize') 47 | self.remoteAddr = self.cfg.get('main', 'remoteHost') 48 | self.localAddr = self.cfg.get('main', 'localHost') 49 | self.localPort = self.cfg.get('main', 'localPort') 50 | self.debug = self.cfg.getboolean('main', 'debug') 51 | self.knownPath = self.cfg.get('main', 'knownPath') 52 | self.addon = self.cfg.get('main', 'addon') 53 | 54 | self.useWebSockets = self.cfg.getboolean('main', 'useWebSockets') 55 | self.localWebSocketPort = self.cfg.get('main', 'localWebSocketPort') 56 | self.localListenPort = self.cfg.get('main', 'localListenPort') 57 | self.localPortOffset = self.cfg.get('main', 'localPortOffset') 58 | self.libDir = self.cfg.get('main', 'libDir') 59 | 60 | # Mail configuration 61 | self.useMail = self.cfg.getboolean('main', 'useMail') 62 | if self.useMail: 63 | self.mailFrom = self.cfg.get('main', 'mailFrom') 64 | self.mailTo = self.cfg.get('main', 'mailTo') 65 | self.SMTPHost = self.cfg.get('main', 'SMTPHost') 66 | 67 | # Add our port offset to all local ports 68 | portOffset = int(self.localPortOffset) 69 | self.localPort = str(int(self.localPort) + portOffset) 70 | self.localWebSocketPort = str(int(self.localWebSocketPort) + portOffset) 71 | self.localListenPort = str(int(self.localListenPort) + portOffset) 72 | 73 | if __name__ == "__main__": 74 | raise Exception("This module cannot run standalone, but is used by ADBFuzz") 75 | -------------------------------------------------------------------------------- /detectors.py.stub: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # ***** BEGIN LICENSE BLOCK ***** 3 | # Version: MPL 2.0 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | # 9 | # The Original Code is ADBFuzz. 10 | # 11 | # The Initial Developer of the Original Code is Christian Holler (decoder). 12 | # 13 | # Contributors: 14 | # Christian Holler (Original Developer) 15 | # 16 | # ***** END LICENSE BLOCK ***** 17 | 18 | 19 | # NOTE: This file is a stub. It will be replaced by our internal implementation 20 | # once that part of the code has been open-sourced. You can however implement 21 | # the functions below easily yourself. 22 | 23 | 24 | from __future__ import with_statement 25 | import os, sys, platform, signal 26 | 27 | class Detector: 28 | def __init__(self): 29 | pass 30 | 31 | class AssertionDetector(Detector): 32 | def __init__(self, knownPath): 33 | pass 34 | 35 | def scanLineAssertions(self, line): 36 | """ 37 | Determines if the given line contains an interesting assertion. 38 | Returns True if the line contains an interesting assertion and 39 | False otherwise. 40 | """ 41 | return True 42 | 43 | def scanFileAssertions(self, currentFile, verbose, ignoreKnownAssertions, lineFilter=None): 44 | """ 45 | Determines if the given file contains one or more assertions. If the lineFilter parameter 46 | is passed, it is interpreted as a function taking one line as a parameter and applied 47 | to every line as a filter prior to checking it. The lineFilter function is expected to 48 | return the filtered line that is checked then. 49 | 50 | Returns an array of all assertions in the file. If ignoreKnownAssertions is True, 51 | then only unknown (interesting) assertions are returned. If there are no assertions, 52 | an empty array is returned instead. 53 | """ 54 | return True 55 | 56 | def hasFatalAssertion(self, currentFile, verbose, lineFilter=None): 57 | """ 58 | Determines if the given file contains a fatal assertion. Parameters are the same as for 59 | the scanFileAssertions method. 60 | 61 | Returns true if a fatal assertion was seen, false if not. 62 | """ 63 | return False 64 | 65 | class CrashDetector(Detector): 66 | def __init__(self, knownPath): 67 | pass 68 | 69 | def isKnownCrashSignature(self, line): 70 | """ 71 | Determines if the given line contains a known crash signature. 72 | Returns True if the line contains a known crash signature and 73 | False otherwise. 74 | """ 75 | return False 76 | -------------------------------------------------------------------------------- /helloworld.cfg: -------------------------------------------------------------------------------- 1 | [main] 2 | # Unique identifier 3 | id = helloworld-fuzz1 4 | 5 | # Fuzzer URL 6 | fuzzer = helloworld.html?wsurl=ws://%(localHost)s:8090/ 7 | 8 | # If the fuzzer does not report anything for this amount 9 | # of time, it will be restarted (defaults to 5 seconds). 10 | #runTimeout = 5 11 | 12 | # If the log grows beyond this size, the fuzzer will be 13 | # restarted automatically. This defaults to 10 kb. 14 | #maxLogSize = 10485760 15 | 16 | # Your IP address in LAN (must be reachable by remote device) 17 | localHost = 10.0.0.1 18 | # Only uncomment the next line, if you're using ADB over TCP/IP rather than USB 19 | #remoteHost = 10.0.0.2 20 | useWebSockets = True 21 | 22 | # Mailer configuration 23 | #useMail = True 24 | #mailFrom = from@example.com 25 | #mailTo = to@example.com 26 | #SMTPHost = smtp.example.com 27 | -------------------------------------------------------------------------------- /helloworld.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 |
11 |

Hello world!

12 |

I'm a square!

13 |

And I'll go crazy now!

14 |
15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /helloworld.js: -------------------------------------------------------------------------------- 1 | /* ***** BEGIN LICENSE BLOCK ***** 2 | * Version: MPL 2.0 3 | * 4 | * This Source Code Form is subject to the terms of the Mozilla Public 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | * 8 | * The Original Code is ADBFuzz. 9 | * 10 | * The Initial Developer of the Original Code is Christian Holler (decoder). 11 | * 12 | * Contributors: 13 | * Christian Holler (Original Developer) 14 | * Jesse Ruderman 15 | * Gary Kwong 16 | * 17 | * ***** END LICENSE BLOCK ***** */ 18 | 19 | /* 20 | * First, we get the output div in our HTML file 21 | * and setup some logging to that for fancy 22 | * visualization 23 | */ 24 | var output = document.getElementById("output"); 25 | output.style.height = '80%'; 26 | var logBuffer = new Array(); 27 | function printOutput(message) { 28 | var pre = document.createElement("p"); 29 | pre.style.wordWrap = "break-word"; 30 | pre.innerHTML = message; 31 | output.appendChild(pre); 32 | 33 | logBuffer.push(pre); 34 | if (logBuffer.length > 10) { 35 | var oldPre = logBuffer.shift(); 36 | output.removeChild(oldPre); 37 | } 38 | } 39 | 40 | var dumpln = function(s) { dump(s + "\n"); printOutput(s); } 41 | 42 | /* Parse arguments passed via URL */ 43 | var args = new Array(); 44 | var params = document.location.href.split('?'); 45 | if (params.length > 1) { 46 | params = params[1].split('&'); 47 | for (pidx in params) { 48 | args[params[pidx].split('=')[0]] = params[pidx].split('=')[1]; 49 | } 50 | } 51 | 52 | /* Set WebSocket URL */ 53 | var wsURL = args['wsurl']; 54 | var ws = undefined; 55 | 56 | /* This function initializes Websocket communication */ 57 | function initWS(startFunc) { 58 | if (wsURL) { 59 | // Firefox doesnt have a WebSocket object 60 | if (typeof(WebSocket) == "undefined") { 61 | if (typeof(MozWebSocket) == "undefined") { 62 | dumpln("Error: No Websocket support found!"); 63 | } else { 64 | WebSocket = MozWebSocket; 65 | } 66 | } 67 | 68 | // Store old dump handler 69 | var consoleDumpln = dumpln; 70 | 71 | // Open websocket connection and assign callbacks 72 | ws = new WebSocket(wsURL); 73 | consoleDumpln("Socket: Attempting connection..."); 74 | ws.onopen = function(evt) { 75 | consoleDumpln("Socked open"); 76 | // Extend dumpln 77 | dumpln = function(s) { ws.send(s + "\n"); consoleDumpln(s); } 78 | startFunc(); 79 | }; 80 | ws.onclose = function(evt) { 81 | consoleDumpln("Socked closed"); 82 | // Restore dumpln 83 | dumpln = consoleDumpln; 84 | }; 85 | ws.onmessage = function(evt) { consoleDumpln("Socked received: " + evt.data); }; 86 | ws.onerror = function(evt) { 87 | consoleDumpln("Socket error: " + evt.data); 88 | // Restore dumpln 89 | dumpln = consoleDumpln; 90 | }; 91 | } 92 | } 93 | 94 | /* Random number generation */ 95 | var MT = new MersenneTwister19937; 96 | var randSeed = Math.floor(Math.random() * Math.pow(2,28)); 97 | dumpln("Seeding PRNG with: " + randSeed); 98 | MT.init_genrand(randSeed); 99 | var rand = function (n) { 100 | return Math.floor(MT.genrand_real2() * n); 101 | }; 102 | 103 | /* Simple function that runs code and logs it */ 104 | function runCode(code) { 105 | try { 106 | /* We need to be able to reproduce, so we need to log our code */ 107 | dumpln("runCode(" + uneval(code) + ");"); 108 | eval(code); 109 | } catch(e) { 110 | dumpln(e.toString()); 111 | } 112 | 113 | } 114 | 115 | /***************************************************************************/ 116 | 117 | /* Get a reference to our pink square :) */ 118 | var square = document.getElementById("square"); 119 | 120 | function start() { 121 | // If there is a websocket URL but no socket, initialize websockets first 122 | // NOTE: start() is called a second time then by the socket's onOpen method. 123 | if (wsURL && ws == undefined) { 124 | return initWS(function() { start(); }); 125 | } 126 | 127 | /* Your work goes here, this is just a stupid demo :D */ 128 | var base = "square.style.MozTransform = '"; 129 | var baseEnd = "';"; 130 | var code = base; 131 | 132 | var num = 1 + rand(3); 133 | for (var i = 0; i < num; ++i) { 134 | switch(rand(4)) { 135 | case 0: code += "rotate(" + rand(360) + "deg" + ")"; break; // Rotate 136 | case 1: code += "scale(" + rand(4) + "," + rand(4) + ")"; break; // Scale 137 | case 2: code += "skew(" + rand(360) + "deg" + "," + rand(360) + "deg" + ")"; break; // Skew 138 | case 3: code += "translate(" + rand(100) + "," + rand(100) + ")"; break; // Translate 139 | } 140 | code += " "; 141 | } 142 | 143 | code += baseEnd; 144 | setTimeout(function() { runCode(code); start(); }, 100); 145 | } 146 | 147 | 148 | /***************************************************************************/ 149 | 150 | 151 | /*************************** 152 | * PRNG Code * 153 | ***************************/ 154 | 155 | // this program is a JavaScript version of Mersenne Twister, with concealment and encapsulation in class, 156 | // an almost straight conversion from the original program, mt19937ar.c, 157 | // translated by y. okada on July 17, 2006. 158 | // Changes by Jesse Ruderman: added "var" keyword in a few spots; added export_mta etc; pasted into fuzz.js. 159 | // in this program, procedure descriptions and comments of original source code were not removed. 160 | // lines commented with //c// were originally descriptions of c procedure. and a few following lines are appropriate JavaScript descriptions. 161 | // lines commented with /* and */ are original comments. 162 | // lines commented with // are additional comments in this JavaScript version. 163 | // before using this version, create at least one instance of MersenneTwister19937 class, and initialize the each state, given below in c comments, of all the instances. 164 | /* 165 | A C-program for MT19937, with initialization improved 2002/1/26. 166 | Coded by Takuji Nishimura and Makoto Matsumoto. 167 | 168 | Before using, initialize the state by using init_genrand(seed) 169 | or init_by_array(init_key, key_length). 170 | 171 | Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, 172 | All rights reserved. 173 | 174 | Redistribution and use in source and binary forms, with or without 175 | modification, are permitted provided that the following conditions 176 | are met: 177 | 178 | 1. Redistributions of source code must retain the above copyright 179 | notice, this list of conditions and the following disclaimer. 180 | 181 | 2. Redistributions in binary form must reproduce the above copyright 182 | notice, this list of conditions and the following disclaimer in the 183 | documentation and/or other materials provided with the distribution. 184 | 185 | 3. The names of its contributors may not be used to endorse or promote 186 | products derived from this software without specific prior written 187 | permission. 188 | 189 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 190 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 191 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 192 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 193 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 194 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 195 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 196 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 197 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 198 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 199 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 200 | 201 | 202 | Any feedback is very welcome. 203 | http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html 204 | email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) 205 | */ 206 | 207 | function MersenneTwister19937() { 208 | /* Period parameters */ 209 | //c//#define N 624 210 | //c//#define M 397 211 | //c//#define MATRIX_A 0x9908b0dfUL /* constant vector a */ 212 | //c//#define UPPER_MASK 0x80000000UL /* most significant w-r bits */ 213 | //c//#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ 214 | var N = 624; 215 | var M = 397; 216 | var MATRIX_A = 0x9908b0df; /* constant vector a */ 217 | var UPPER_MASK = 0x80000000; /* most significant w-r bits */ 218 | var LOWER_MASK = 0x7fffffff; /* least significant r bits */ 219 | //c//static unsigned long mt[N]; /* the array for the state vector */ 220 | //c//static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ 221 | var mt = new Array(N); /* the array for the state vector */ 222 | var mti = N+1; /* mti==N+1 means mt[N] is not initialized */ 223 | 224 | function unsigned32 (n1) // returns a 32-bits unsiged integer from an operand to which applied a bit operator. 225 | { 226 | return n1 < 0 ? (n1 ^ UPPER_MASK) + UPPER_MASK : n1; 227 | } 228 | 229 | function subtraction32 (n1, n2) // emulates lowerflow of a c 32-bits unsiged integer variable, instead of the operator -. these both arguments must be non-negative integers expressible using unsigned 32 bits. 230 | { 231 | return n1 < n2 ? unsigned32((0x100000000 - (n2 - n1)) & 0xffffffff) : n1 - n2; 232 | } 233 | 234 | function addition32 (n1, n2) // emulates overflow of a c 32-bits unsiged integer variable, instead of the operator +. these both arguments must be non-negative integers expressible using unsigned 32 bits. 235 | { 236 | return unsigned32((n1 + n2) & 0xffffffff) 237 | } 238 | 239 | function multiplication32 (n1, n2) // emulates overflow of a c 32-bits unsiged integer variable, instead of the operator *. these both arguments must be non-negative integers expressible using unsigned 32 bits. 240 | { 241 | var sum = 0; 242 | for (var i = 0; i < 32; ++i){ 243 | if ((n1 >>> i) & 0x1){ 244 | sum = addition32(sum, unsigned32(n2 << i)); 245 | } 246 | } 247 | return sum; 248 | } 249 | 250 | /* initializes mt[N] with a seed */ 251 | //c//void init_genrand(unsigned long s) 252 | this.init_genrand = function (s) 253 | { 254 | //c//mt[0]= s & 0xffffffff; 255 | mt[0]= unsigned32(s & 0xffffffff); 256 | for (mti=1; mti> 30)) + mti); 259 | addition32(multiplication32(1812433253, unsigned32(mt[mti-1] ^ (mt[mti-1] >>> 30))), mti); 260 | /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ 261 | /* In the previous versions, MSBs of the seed affect */ 262 | /* only MSBs of the array mt[]. */ 263 | /* 2002/01/09 modified by Makoto Matsumoto */ 264 | //c//mt[mti] &= 0xffffffff; 265 | mt[mti] = unsigned32(mt[mti] & 0xffffffff); 266 | /* for >32 bit machines */ 267 | } 268 | } 269 | 270 | /* initialize by an array with array-length */ 271 | /* init_key is the array for initializing keys */ 272 | /* key_length is its length */ 273 | /* slight change for C++, 2004/2/26 */ 274 | //c//void init_by_array(unsigned long init_key[], int key_length) 275 | this.init_by_array = function (init_key, key_length) 276 | { 277 | //c//int i, j, k; 278 | var i, j, k; 279 | //c//init_genrand(19650218); 280 | this.init_genrand(19650218); 281 | i=1; j=0; 282 | k = (N>key_length ? N : key_length); 283 | for (; k; k--) { 284 | //c//mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525)) 285 | //c// + init_key[j] + j; /* non linear */ 286 | mt[i] = addition32(addition32(unsigned32(mt[i] ^ multiplication32(unsigned32(mt[i-1] ^ (mt[i-1] >>> 30)), 1664525)), init_key[j]), j); 287 | mt[i] = 288 | //c//mt[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */ 289 | unsigned32(mt[i] & 0xffffffff); 290 | i++; j++; 291 | if (i>=N) { mt[0] = mt[N-1]; i=1; } 292 | if (j>=key_length) j=0; 293 | } 294 | for (k=N-1; k; k--) { 295 | //c//mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941)) 296 | //c//- i; /* non linear */ 297 | mt[i] = subtraction32(unsigned32((dbg=mt[i]) ^ multiplication32(unsigned32(mt[i-1] ^ (mt[i-1] >>> 30)), 1566083941)), i); 298 | //c//mt[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */ 299 | mt[i] = unsigned32(mt[i] & 0xffffffff); 300 | i++; 301 | if (i>=N) { mt[0] = mt[N-1]; i=1; } 302 | } 303 | mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */ 304 | } 305 | 306 | this.export_state = function() { return [mt, mti]; }; 307 | this.import_state = function(s) { mt = s[0]; mti = s[1]; }; 308 | this.export_mta = function() { return mt; }; 309 | this.import_mta = function(_mta) { mt = _mta }; 310 | this.export_mti = function() { return mti; }; 311 | this.import_mti = function(_mti) { mti = _mti; } 312 | 313 | /* generates a random number on [0,0xffffffff]-interval */ 314 | //c//unsigned long genrand_int32(void) 315 | this.genrand_int32 = function () 316 | { 317 | //c//unsigned long y; 318 | //c//static unsigned long mag01[2]={0x0UL, MATRIX_A}; 319 | var y; 320 | var mag01 = new Array(0x0, MATRIX_A); 321 | /* mag01[x] = x * MATRIX_A for x=0,1 */ 322 | 323 | if (mti >= N) { /* generate N words at one time */ 324 | //c//int kk; 325 | var kk; 326 | 327 | if (mti == N+1) /* if init_genrand() has not been called, */ 328 | //c//init_genrand(5489); /* a default initial seed is used */ 329 | this.init_genrand(5489); /* a default initial seed is used */ 330 | 331 | for (kk=0;kk> 1) ^ mag01[y & 0x1]; 334 | y = unsigned32((mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK)); 335 | mt[kk] = unsigned32(mt[kk+M] ^ (y >>> 1) ^ mag01[y & 0x1]); 336 | } 337 | for (;kk> 1) ^ mag01[y & 0x1]; 340 | y = unsigned32((mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK)); 341 | mt[kk] = unsigned32(mt[kk+(M-N)] ^ (y >>> 1) ^ mag01[y & 0x1]); 342 | } 343 | //c//y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); 344 | //c//mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1]; 345 | y = unsigned32((mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK)); 346 | mt[N-1] = unsigned32(mt[M-1] ^ (y >>> 1) ^ mag01[y & 0x1]); 347 | mti = 0; 348 | } 349 | 350 | y = mt[mti++]; 351 | 352 | /* Tempering */ 353 | //c//y ^= (y >> 11); 354 | //c//y ^= (y << 7) & 0x9d2c5680; 355 | //c//y ^= (y << 15) & 0xefc60000; 356 | //c//y ^= (y >> 18); 357 | y = unsigned32(y ^ (y >>> 11)); 358 | y = unsigned32(y ^ ((y << 7) & 0x9d2c5680)); 359 | y = unsigned32(y ^ ((y << 15) & 0xefc60000)); 360 | y = unsigned32(y ^ (y >>> 18)); 361 | 362 | return y; 363 | } 364 | 365 | /* generates a random number on [0,0x7fffffff]-interval */ 366 | //c//long genrand_int31(void) 367 | this.genrand_int31 = function () 368 | { 369 | //c//return (genrand_int32()>>1); 370 | return (this.genrand_int32()>>>1); 371 | } 372 | 373 | /* generates a random number on [0,1]-real-interval */ 374 | //c//double genrand_real1(void) 375 | this.genrand_real1 = function () 376 | { 377 | //c//return genrand_int32()*(1.0/4294967295.0); 378 | return this.genrand_int32()*(1.0/4294967295.0); 379 | /* divided by 2^32-1 */ 380 | } 381 | 382 | /* generates a random number on [0,1)-real-interval */ 383 | //c//double genrand_real2(void) 384 | this.genrand_real2 = function () 385 | { 386 | //c//return genrand_int32()*(1.0/4294967296.0); 387 | return this.genrand_int32()*(1.0/4294967296.0); 388 | /* divided by 2^32 */ 389 | } 390 | 391 | /* generates a random number on (0,1)-real-interval */ 392 | //c//double genrand_real3(void) 393 | this.genrand_real3 = function () 394 | { 395 | //c//return ((genrand_int32()) + 0.5)*(1.0/4294967296.0); 396 | return ((this.genrand_int32()) + 0.5)*(1.0/4294967296.0); 397 | /* divided by 2^32 */ 398 | } 399 | 400 | /* generates a random number on [0,1) with 53-bit resolution*/ 401 | //c//double genrand_res53(void) 402 | this.genrand_res53 = function () 403 | { 404 | //c//unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6; 405 | var a=this.genrand_int32()>>>5, b=this.genrand_int32()>>>6; 406 | return(a*67108864.0+b)*(1.0/9007199254740992.0); 407 | } 408 | /* These real versions are due to Isaku Wada, 2002/01/09 added */ 409 | } 410 | 411 | /*************************** 412 | * End of PRNG Code * 413 | ***************************/ 414 | 415 | 416 | /* For reproduction, your logged code will go here, see README */ 417 | // SPLICE 418 | start(); 419 | // SPLICE 420 | -------------------------------------------------------------------------------- /logfilter.py: -------------------------------------------------------------------------------- 1 | # ***** BEGIN LICENSE BLOCK ***** 2 | # Version: MPL 2.0 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | # The Original Code is ADBFuzz. 9 | # 10 | # The Initial Developer of the Original Code is Christian Holler (decoder). 11 | # 12 | # Contributors: 13 | # Christian Holler (Original Developer) 14 | # 15 | # ***** END LICENSE BLOCK ***** 16 | 17 | import threading 18 | import subprocess 19 | 20 | class LogFilter(threading.Thread): 21 | def __init__(self, config, triager, logProcess, logFile): 22 | self.config = config 23 | self.triager = triager 24 | self.logProcess = logProcess 25 | self.logFile = logFile 26 | self.eof = False 27 | 28 | # Thread initialization stuff 29 | self.stdout = None 30 | self.stderr = None 31 | threading.Thread.__init__(self) 32 | 33 | # We need this to know when to terminate 34 | self._stop = threading.Event() 35 | 36 | def run(self): 37 | if self.logFile == None: 38 | logFileFd = None 39 | else: 40 | logFileFd = open(self.logFile, 'w') 41 | 42 | # Loop until we get aborted, hit EOF or find something interesting 43 | while not self._stop.isSet(): 44 | line = self.logProcess.stdout.readline() 45 | if (len(line) == 0): 46 | self.eof = True 47 | break 48 | 49 | if logFileFd == None: 50 | # Output to stdout 51 | print line.strip() 52 | else: 53 | # Store to file first 54 | logFileFd.write(line) 55 | 56 | # Now check if it has something interesting (e.g. assertion) 57 | line = line.strip() 58 | if self.triager.checkLine(line): 59 | break 60 | 61 | if logFileFd != None: 62 | logFileFd.close() 63 | 64 | return 65 | 66 | def terminate(self): 67 | self._stop.set() 68 | 69 | if __name__ == "__main__": 70 | raise Exception("This module cannot run standalone, but is used by ADBFuzz") 71 | -------------------------------------------------------------------------------- /mail.py: -------------------------------------------------------------------------------- 1 | # ***** BEGIN LICENSE BLOCK ***** 2 | # Version: MPL 2.0 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | # The Original Code is ADBFuzz. 9 | # 10 | # The Initial Developer of the Original Code is Christian Holler (decoder). 11 | # 12 | # Contributors: 13 | # Christian Holler (Original Developer) 14 | # 15 | # ***** END LICENSE BLOCK ***** 16 | 17 | import smtplib 18 | 19 | class Mailer: 20 | def __init__(self, config): 21 | self.config = config 22 | 23 | def notify(self, issueUUID, issueDesc, miniDump): 24 | 25 | msg = ("From: %s\r\nTo: %s\r\n" % (self.config.mailFrom, self.config.mailTo)) 26 | msg = msg + "Subject: [ADBFuzz] Issue report: " + issueDesc + "\r\n\r\n" 27 | msg = msg + "Crash UUID: " + issueUUID + "\r\n" 28 | msg = msg + "Instance identifier: " + self.config.id + "\r\n" 29 | msg = msg + "\r\n" 30 | 31 | if miniDump != None: 32 | msg = msg + "Crash trace:" + "\r\n" 33 | 34 | crashTrace = miniDump.getSymbolizedCrashTrace() 35 | 36 | for (frameNum, frameAddr, frameFile) in crashTrace: 37 | msg = msg + " " + frameNum + " " + frameAddr + " " + frameFile + "\r\n" 38 | 39 | server = smtplib.SMTP(self.config.SMTPHost) 40 | server.set_debuglevel(1) 41 | server.sendmail(self.config.mailFrom, [ self.config.mailTo ], msg) 42 | server.quit() 43 | 44 | return 45 | 46 | if __name__ == "__main__": 47 | raise Exception("This module cannot run standalone, but is used by ADBFuzz") 48 | -------------------------------------------------------------------------------- /minidump.py: -------------------------------------------------------------------------------- 1 | # ***** BEGIN LICENSE BLOCK ***** 2 | # Version: MPL 2.0 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | # The Original Code is ADBFuzz. 9 | # 10 | # The Initial Developer of the Original Code is Christian Holler (decoder). 11 | # 12 | # Contributors: 13 | # Christian Holler (Original Developer) 14 | # 15 | # ***** END LICENSE BLOCK ***** 16 | 17 | import subprocess 18 | import os 19 | import sys 20 | 21 | class Minidump: 22 | def __init__(self, dumpFile, libDir=None): 23 | self.dumpFile = dumpFile 24 | self.libDir = libDir 25 | self.cleaned = False 26 | 27 | self.crashTrace = [] 28 | self.crashType = None 29 | self.crashThread = None 30 | self.crashTraceSymbols = [] 31 | 32 | def getCrashTrace(self): 33 | # Return cached result if available 34 | if (len(self.crashTrace) > 0): 35 | return self.crashTrace 36 | 37 | proc = subprocess.Popen(["minidump_stackwalk", "-m", self.getFilename()], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 38 | stdout = proc.communicate()[0].splitlines() 39 | 40 | for line in stdout: 41 | # Search for Crash|SIGABRT|0x43e0|9 42 | if line.startswith("Crash|SIG"): 43 | tok = line.split("|") 44 | self.crashType = tok[1] 45 | self.crashThread = tok[3] 46 | break 47 | 48 | if self.crashThread == None: 49 | raise Exception("Cannot identify crashing thread from dump") 50 | 51 | for line in stdout: 52 | tok = line.split("|") 53 | if ((len(tok) == 7) and (tok[0] == self.crashThread)): 54 | if (int(tok[1]) < 8): 55 | self.crashTrace.append((tok[1], tok[2], tok[6])) 56 | 57 | return self.crashTrace 58 | 59 | def getCrashType(self): 60 | if (self.crashType == None): 61 | # Will cache the crash type 62 | self.getCrashTrace() 63 | 64 | return self.crashType 65 | 66 | def getCrashingThread(self): 67 | if (self.crashThread == None): 68 | # Will cache the crashing thread 69 | self.getCrashTrace() 70 | 71 | return self.crashThread 72 | 73 | def getSymbolizedCrashTrace(self): 74 | # Return cached result if available 75 | if (len(self.crashTraceSymbols) > 0): 76 | return self.crashTraceSymbols 77 | 78 | dumpTrace = self.getCrashTrace() 79 | 80 | for frame in dumpTrace: 81 | frameNum = frame[0] 82 | frameFile = frame[1] 83 | frameAddr = frame[2] 84 | frameFileResolved = None 85 | for path, dirs, files in os.walk(os.path.abspath(self.libDir)): 86 | for filename in files: 87 | if filename == frameFile: 88 | frameFileResolved = os.path.join(path, filename) 89 | break 90 | 91 | if frameFileResolved != None: 92 | addr2line = subprocess.Popen(["addr2line", "-f", "-C", "-e", frameFileResolved, frameAddr], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 93 | a2ldata = addr2line.communicate()[0].splitlines() 94 | if (len(a2ldata) >= 2): 95 | frameFunc = a2ldata[0] 96 | frameSrc = a2ldata[1] 97 | self.crashTraceSymbols.append((frameNum, frameFunc, frameSrc)) 98 | continue 99 | elif (len(a2ldata) == 1): 100 | frameFunc = a2ldata[0] 101 | self.crashTraceSymbols.append((frameNum, frameFunc, frameFile)) 102 | continue 103 | 104 | self.crashTraceSymbols.append((frameNum, frameAddr, frameFile)) 105 | 106 | return self.crashTraceSymbols 107 | 108 | def cleanup(self): 109 | os.remove(self.getFilename()) 110 | self.cleaned = True 111 | 112 | def getFilename(self): 113 | if self.cleaned: 114 | raise Exception("Attempted to perform file operation on deleted minidump file") 115 | else: 116 | return self.dumpFile 117 | 118 | if __name__ == "__main__": 119 | raise Exception("This module cannot run standalone, but is used by ADBFuzz") 120 | -------------------------------------------------------------------------------- /misc/prefs.js: -------------------------------------------------------------------------------- 1 | # Mozilla User Preferences 2 | 3 | /* Do not edit this file. 4 | * 5 | * If you make changes to this file while the application is running, 6 | * the changes will be overwritten when the application exits. 7 | * 8 | * To make a manual change to preferences, you can visit the URL about:config 9 | * For more information, see http://www.mozilla.org/unix/customizing.html#prefs 10 | */ 11 | 12 | user_pref("app.update.lastUpdateTime.addon-background-update-timer", 2600380); 13 | user_pref("app.update.lastUpdateTime.auto-addon-background-update-timer", 2600380); 14 | user_pref("app.update.lastUpdateTime.blocklist-background-update-timer", 2600380); 15 | user_pref("app.update.lastUpdateTime.search-engine-update-timer", 2600380); 16 | user_pref("extensions.blocklist.pingCountVersion", 0); 17 | user_pref("extensions.lastAppVersion", "13.0a1"); 18 | user_pref("extensions.lastPlatformVersion", "13.0a1"); 19 | user_pref("extensions.shownSelectionUI", true); 20 | user_pref("xpinstall.whitelist.add", ""); 21 | user_pref("browser.sessionstore.resume_from_crash", false); 22 | user_pref("browser.startup.homepage", "about:blank"); 23 | user_pref("browser.console.showInPanel", true); 24 | user_pref("browser.dom.window.dump.enabled", true); 25 | user_pref("browser.firstrun.show.localepicker", false); 26 | user_pref("browser.firstrun.show.uidiscovery", false); 27 | user_pref("browser.ui.layout.tablet", 0); // force tablet UI off 28 | user_pref("dom.allow_scripts_to_close_windows", true); 29 | user_pref("dom.disable_open_during_load", false); 30 | user_pref("dom.max_script_run_time", 0); // no slow script dialogs 31 | user_pref("hangmonitor.timeout", 0); // no hang monitor 32 | user_pref("dom.max_chrome_script_run_time", 0); 33 | user_pref("dom.popup_maximum", -1); 34 | user_pref("dom.send_after_paint_to_content", true); 35 | user_pref("dom.successive_dialog_time_limit", 0); 36 | user_pref("signed.applets.codebase_principal_support", true); 37 | user_pref("security.warn_submit_insecure", false); 38 | user_pref("browser.shell.checkDefaultBrowser", false); 39 | user_pref("shell.checkDefaultClient", false); 40 | user_pref("browser.warnOnQuit", false); 41 | user_pref("accessibility.typeaheadfind.autostart", false); 42 | user_pref("javascript.options.showInConsole", true); 43 | user_pref("devtools.errorconsole.enabled", true); 44 | user_pref("layout.debug.enable_data_xbl", true); 45 | user_pref("browser.EULA.override", true); 46 | user_pref("javascript.options.jit_hardening", true); 47 | user_pref("gfx.color_management.force_srgb", true); 48 | user_pref("network.manage-offline-status", false); 49 | user_pref("test.mousescroll", true); 50 | user_pref("security.default_personal_cert", "Select Automatically"); // Need to client auth test be w/o any dialogs 51 | user_pref("network.http.prompt-temp-redirect", false); 52 | user_pref("media.cache_size", 100); 53 | user_pref("security.warn_viewing_mixed", false); 54 | user_pref("app.update.enabled", false); 55 | user_pref("browser.panorama.experienced_first_run", true); // Assume experienced 56 | user_pref("dom.w3c_touch_events.enabled", true); 57 | user_pref("toolkit.telemetry.prompted", true); 58 | user_pref("toolkit.telemetry.enabled", false); 59 | -------------------------------------------------------------------------------- /tools/compose.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import subprocess 4 | import time 5 | import shutil 6 | import os 7 | import sys 8 | 9 | def main(): 10 | logfile = sys.argv[1] 11 | fuzzFile = sys.argv[2] 12 | searchStr = sys.argv[3] 13 | logLines = [] 14 | 15 | log = open(logfile, "r") 16 | rawLogLines = log.readlines() 17 | log.close() 18 | 19 | for rawLogLine in rawLogLines: 20 | idx = rawLogLine.find(searchStr) 21 | if (idx > -1): 22 | rawLogLine = rawLogLine[idx:] 23 | # Remove the nasty \r from adb shell output 24 | logLines.append(rawLogLine.translate(None, '\r')) 25 | 26 | jsf = open(fuzzFile, "r") 27 | jsfo = open("testmin.js", "w") 28 | skipTillSplice = False 29 | for line in jsf.readlines(): 30 | if skipTillSplice and not line.find("SPLICE") > -1: 31 | next 32 | elif line.find("SPLICE") > -1: 33 | if skipTillSplice: 34 | skipTillSplice = False 35 | else: 36 | jsfo.writelines(logLines) 37 | skipTillSplice = True 38 | else: 39 | jsfo.write(line) 40 | 41 | jsf.close() 42 | jsfo.close() 43 | 44 | if __name__ == "__main__": 45 | main() 46 | -------------------------------------------------------------------------------- /tools/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import subprocess 5 | 6 | def main(): 7 | testFile = sys.argv[1] 8 | 9 | if (testFile.endswith('.log')): 10 | # First, compose the test from the log 11 | subprocess.call(["python", "tools/compose.py", testFile, os.environ['FUZZFILE']]) 12 | testFile = "testmin.html" 13 | 14 | exit(subprocess.call(["python", "adbfuzz.py", "reproduce", testFile, os.environ['TIMEOUT']])) 15 | 16 | if __name__ == "__main__": 17 | main() 18 | -------------------------------------------------------------------------------- /triage.py: -------------------------------------------------------------------------------- 1 | # ***** BEGIN LICENSE BLOCK ***** 2 | # Version: MPL 2.0 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | # The Original Code is ADBFuzz. 9 | # 10 | # The Initial Developer of the Original Code is Christian Holler (decoder). 11 | # 12 | # Contributors: 13 | # Christian Holler (Original Developer) 14 | # 15 | # ***** END LICENSE BLOCK ***** 16 | 17 | import subprocess 18 | import time 19 | import shutil 20 | import os 21 | import signal 22 | import sys 23 | import re 24 | 25 | from detectors import AssertionDetector, CrashDetector 26 | from mail import Mailer 27 | 28 | class Triager: 29 | def __init__(self, config): 30 | self.config = config 31 | 32 | if self.config.useMail: 33 | self.mailer = Mailer(config) 34 | 35 | self.assertDetector = AssertionDetector(self.config.knownPath) 36 | self.crashDetector = CrashDetector(self.config.knownPath) 37 | 38 | self.androidLogLinefilter = lambda x: re.sub('^[^:]+: ', '', x) 39 | 40 | def process(self, issueUUID, miniDump, systemLog, websockLog): 41 | print "Triaging crash..." 42 | 43 | # Read Android system log 44 | systemLogFile = open(systemLog) 45 | 46 | # Check if we got aborted or crashed 47 | aborted = self.assertDetector.hasFatalAssertion( 48 | systemLogFile, 49 | verbose=True, 50 | lineFilter=self.androidLogLinefilter 51 | ) 52 | 53 | # Reopen file 54 | systemLogFile.close() 55 | systemLogFile = open(systemLog) 56 | 57 | # Check if the syslog file contains an interesting assertion. 58 | # The lambda removes the Android syslog tags before matching 59 | assertions = self.assertDetector.scanFileAssertions( 60 | systemLogFile, 61 | verbose=True, 62 | ignoreKnownAssertions=True, 63 | lineFilter=self.androidLogLinefilter 64 | ) 65 | 66 | hasNewAssertion = len(assertions) > 0 67 | 68 | systemLogFile.close() 69 | 70 | if miniDump == None and not hasNewAssertion: 71 | print "Error: No minidump available but also no assertions detected!" 72 | return 73 | 74 | isNewCrash = (miniDump != None) 75 | crashFunction = "Unknown" 76 | issueDesc = "Unknown" 77 | 78 | if miniDump != None: 79 | # Obtain symbolized crash trace to check crash signature 80 | trace = miniDump.getSymbolizedCrashTrace() 81 | 82 | if (len(trace) > 0): 83 | crashFunction = trace[0][1] 84 | issueDesc = "Crashed at " + crashFunction 85 | isNewCrash = not self.crashDetector.isKnownCrashSignature(crashFunction) 86 | # Also check first frame (some functions are blacklisted here) 87 | if (isNewCrash and len(trace) > 1): 88 | isNewCrash = not self.crashDetector.isKnownCrashSignature(trace[1][1]) 89 | 90 | # Use the last assertion as issue description 91 | if hasNewAssertion: 92 | issueDesc = assertions[len(assertions)-1] 93 | 94 | print issueDesc 95 | 96 | if hasNewAssertion or (not aborted and isNewCrash): 97 | print "Found new issue, check " + websockLog + " to reproduce" 98 | if self.config.useMail: 99 | self.mailer.notify(issueUUID, issueDesc, miniDump) 100 | else: 101 | # Delete files if not in debug mode 102 | if not self.config.debug: 103 | if miniDump != None: 104 | miniDump.cleanup() 105 | os.remove(systemLog) 106 | os.remove(websockLog) 107 | 108 | return 109 | 110 | def checkLine(self, line): 111 | return self.assertDetector.scanLineAssertions(self.androidLogLinefilter(line)) 112 | 113 | 114 | if __name__ == "__main__": 115 | raise Exception("This module cannot run standalone, but is used by ADBFuzz") 116 | -------------------------------------------------------------------------------- /websocklog.py: -------------------------------------------------------------------------------- 1 | # ***** BEGIN LICENSE BLOCK ***** 2 | # Version: MPL 2.0 3 | # 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | # 8 | # The Original Code is ADBFuzz. 9 | # 10 | # The Initial Developer of the Original Code is Christian Holler (decoder). 11 | # 12 | # Contributors: 13 | # Christian Holler (Original Developer) 14 | # 15 | # ***** END LICENSE BLOCK ***** 16 | 17 | import sys 18 | from socket import * 19 | 20 | if __name__ == "__main__": 21 | HOST, PORT = sys.argv[1], sys.argv[2] 22 | s = socket(AF_INET, SOCK_STREAM) 23 | s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 24 | s.bind((HOST, int(PORT))) 25 | s.listen(1) 26 | 27 | while True: 28 | conn, addr = s.accept() 29 | print "[WebSockLog - Client Connect] %s" % str(addr) 30 | fileHandle = open("websock.log", "w") 31 | sockfile = conn.makefile() 32 | while True: 33 | data = sockfile.readline() 34 | if (len(data) == 0): 35 | break 36 | data = data.strip() 37 | fileHandle.write(data + "\n") 38 | fileHandle.flush() 39 | --------------------------------------------------------------------------------