├── .gitattributes ├── README.md ├── certificate.py └── pyfiddler.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.pyproj diff=csharpproject 7 | 8 | # Standard to msysgit 9 | *.doc diff=astextplain 10 | *.DOC diff=astextplain 11 | *.docx diff=astextplain 12 | *.DOCX diff=astextplain 13 | *.dot diff=astextplain 14 | *.DOT diff=astextplain 15 | *.pdf diff=astextplain 16 | *.PDF diff=astextplain 17 | *.rtf diff=astextplain 18 | *.RTF diff=astextplain 19 | 20 | # Custom for this repository 21 | *.json diff=jsonfile 22 | *.pyc diff=python running code 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyfiddler 2 | Using Python to call FiddlerCore and capture both HTTP and HTTPS packets. 3 | 4 | Auto supply the certificate when capture HTTPS . Use Bouncy Castle certificate generator to generates a root certificate and asks to trust it, then generates end-entity certificates on-the-fly for each domain visited with the root certificate as the signer. 5 | 6 | - UsesCase:
7 | Put FiddlerCore4.dll in any path and append it to Python sys.path to reference it
8 | Put CertMaker.dll and BCMakeCert.dll to Python bin directory like C:\python27
9 | 10 | - Dependence:
11 | pythonnet
12 | pypiwin32
13 | 14 | - Test environment:
15 | Python version: 2.7.13
16 | Pythonnet version: 2.3.0
17 | FiddlerCore 4.6.20171.13571
18 | Win7 32bit and xp sp3 with .net 4
19 | 20 | - Thanks:
21 | [Rick Strahl](https://weblog.west-wind.com/posts/2014/jul/29/using-fiddlercore-to-capture-http-requests-with-net)
22 | [Eric Lawrence](http://www.telerik.com/blogs/understanding-fiddler-certificate-generators) 23 | 24 | -------------------------------------------------------------------------------- /certificate.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | # To use Bouncy Castle certificate generator both needs CertMaker.dll and BCMakeCert.dll 3 | 4 | import pickle 5 | 6 | def prepareCert(FC): 7 | try: 8 | with open('cert', 'rb') as handle: 9 | certificate = pickle.load(handle) 10 | if certificate: 11 | FC.FiddlerApplication.Prefs.SetStringPref("fiddler.certmaker.bc.key", certificate['key']) 12 | FC.FiddlerApplication.Prefs.SetStringPref("fiddler.certmaker.bc.cert", certificate['cert']) 13 | except IOError as err: 14 | print"\n!! File Error:"+str(err) 15 | 16 | # when json file above load and call SetStringPref() method 17 | # FC.CertMaker.rootCertExists() will be True 18 | if not FC.CertMaker.rootCertExists(): 19 | FC.CertMaker.createRootCert() 20 | FC.CertMaker.trustRootCert() 21 | 22 | cert = FC.FiddlerApplication.Prefs.GetStringPref("fiddler.certmaker.bc.cert", None) 23 | key = FC.FiddlerApplication.Prefs.GetStringPref("fiddler.certmaker.bc.key", None) 24 | certificate = {'key':key,'cert':cert} 25 | with open('cert', 'wb') as handle: 26 | pickle.dump(certificate, handle, protocol=pickle.HIGHEST_PROTOCOL) 27 | 28 | else: 29 | print "\n!! Root Certificate Exists **" 30 | -------------------------------------------------------------------------------- /pyfiddler.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | import clr 4 | import sys 5 | import win32api 6 | import win32con 7 | from datetime import datetime 8 | from certificate import * 9 | 10 | sys.path.append("C:\\api") 11 | clr.FindAssembly("FiddlerCore4") 12 | clr.AddReference("FiddlerCore4") 13 | import Fiddler as FC 14 | 15 | # do some thing when Ctrl-c or colse 16 | def onClose(sig): 17 | print chr(7) 18 | FC.FiddlerApplication.Shutdown() 19 | win32api.MessageBox(win32con.NULL, 'See you later', 'Exit', win32con.MB_OK) 20 | 21 | 22 | # will be invoked when it is called by delegate. 23 | def printLog(source,oLEA): 24 | print "\n** LogString: **\n" + oLEA.LogString 25 | 26 | def printSession(s): 27 | 28 | if s is None or s.oRequest is None or s.oRequest.headers is None: 29 | return 30 | 31 | # Ignore HTTPS connect requests 32 | if s.RequestMethod == "CONNECT": 33 | return 34 | 35 | # Filter for host 36 | host_obmit = "123.123.123.123" 37 | host = s.hostname.lower() 38 | if host_obmit not in host: 39 | return 40 | 41 | # Filter for path 42 | url = s.url.lower() 43 | if '/path' not in url: 44 | return 45 | 46 | datetime_now = datetime.now().strftime('%a, %Y %b %d %H:%M:%S') 47 | datetime_now_utc = datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT') 48 | 49 | reqHeaders = s.oRequest.headers.ToString() 50 | reqBody = s.GetRequestBodyAsString() 51 | respCode = s.responseCode 52 | respHeaders = s.oResponse.headers.ToString() 53 | 54 | print '--->' 55 | print datetime_now 56 | print reqHeaders 57 | 58 | # deal with cookie 59 | if s.oRequest.headers.Exists("cookie"): 60 | cookie = s.oRequest.headers.AllValues("cookie") 61 | print "Request cookie are:",cookie 62 | 63 | #if reqBody: print "!! Request body:\n",reqBody 64 | print '<---' 65 | print respCode 66 | 67 | def fiddler(FC,flags): 68 | # register event handler 69 | # object.SomeEvent += handler 70 | # 71 | # unregister event handler 72 | # object.SomeEvent -= handler 73 | # 74 | # passed a callable Python object to get a delegate instance. 75 | FC.FiddlerApplication.Log.OnLogString += printLog 76 | FC.FiddlerApplication.AfterSessionComplete += printSession 77 | 78 | # When decrypting HTTPS traffic,ignore the server certificate errors 79 | FC.CONFIG.IgnoreServerCertErrors = False 80 | 81 | # start up capture 82 | FC.FiddlerApplication.Startup(8888, flags) 83 | 84 | 85 | if __name__ == '__main__': 86 | 87 | win32api.SetConsoleCtrlHandler(onClose, 1) 88 | captureType = "http" 89 | 90 | #RegisterAsSystemProxy:1 91 | #OptimizeThreadPool:512 92 | #MonitorAllConnections:32 93 | #DecryptSSL:2 94 | #AllowRemoteClients:8 95 | 96 | if captureType == "https": 97 | prepareCert(FC) 98 | fiddler(FC, 1+512+32+2) 99 | else: 100 | fiddler(FC, 1+512+32) 101 | try: 102 | # keep console window be open 103 | raw_input() 104 | except: 105 | pass 106 | --------------------------------------------------------------------------------