├── BridaLogo.png ├── BridaScreen1.PNG ├── Demo ├── Android │ ├── DemoAndroidBridaJS │ │ ├── androidDefaultHooks.js │ │ ├── brida.js │ │ ├── bridaFunctions.js │ │ └── iosDefaultHooks.js │ ├── HIP2021_demo.apk │ ├── README.md │ ├── certs │ │ ├── 9e487db2.0 │ │ ├── ca-cert.pem │ │ ├── server-cert.pem │ │ └── server-key.pem │ ├── exportedPluginsDemoAndroid.csv │ └── server.py └── iOS │ ├── BridaTestAppBackend-0.0.1-SNAPSHOT.jar │ ├── DemoHIP.ipa │ ├── DemoHITB.ipa │ ├── DemoIOSBridaJS │ ├── androidDefaultHooks.js │ ├── brida.js │ ├── bridaFunctions.js │ └── iosDefaultHooks.js │ ├── OldCompiledPlugins │ ├── BridaDemoLoginPlugin-0.0.1-SNAPSHOT-jar-with-dependencies.jar │ ├── BridaDemoSearchPlugin-0.0.1-SNAPSHOT-jar-with-dependencies.jar │ └── Sources │ │ ├── BridaDemoLoginPlugin │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ └── java │ │ │ └── burp │ │ │ └── BurpExtender.java │ │ └── BridaDemoSearchPlugin │ │ ├── pom.xml │ │ └── src │ │ └── main │ │ └── java │ │ └── burp │ │ └── BurpExtender.java │ ├── README.md │ ├── dbBackendBridaDemo.db │ └── exportedPluginsDemoIOS.csv ├── LICENSE ├── README.md ├── images ├── Components1.PNG ├── Components10.PNG ├── Components11.PNG ├── Components2.PNG ├── Components3.PNG ├── Components4.PNG ├── Components5.PNG ├── Components6.PNG ├── Components7.PNG ├── Components8.PNG ├── Components9.PNG ├── DebugExport1.PNG ├── GenerateStub1.PNG ├── GenerateStub2.PNG ├── GraphicalAnalysis1.PNG ├── GraphicalAnalysis2.PNG ├── GraphicalAnalysis3.PNG ├── GraphicalAnalysis4.PNG ├── GraphicalAnalysis5.PNG ├── GraphicalAnalysis6.PNG ├── GraphicalAnalysis7.PNG ├── GraphicalAnalysis8.PNG ├── GraphicalAnalysis9.PNG ├── HooksAndFunctions1.PNG ├── HooksAndFunctions2.PNG ├── IContextMenu_Plugin_1.PNG ├── IContextMenu_Plugin_10.PNG ├── IContextMenu_Plugin_2.PNG ├── IContextMenu_Plugin_3.PNG ├── IContextMenu_Plugin_4.PNG ├── IContextMenu_Plugin_5.PNG ├── IContextMenu_Plugin_6.PNG ├── IContextMenu_Plugin_7.PNG ├── IContextMenu_Plugin_8.PNG ├── IContextMenu_Plugin_9.PNG ├── IHttpListener_Plugin_0.PNG ├── IHttpListener_Plugin_1.PNG ├── IHttpListener_Plugin_10.PNG ├── IHttpListener_Plugin_11.PNG ├── IHttpListener_Plugin_2.PNG ├── IHttpListener_Plugin_3.PNG ├── IHttpListener_Plugin_4.PNG ├── IHttpListener_Plugin_5.PNG ├── IHttpListener_Plugin_6.PNG ├── IHttpListener_Plugin_7.PNG ├── IHttpListener_Plugin_8.PNG ├── IHttpListener_Plugin_9.PNG ├── IMessageEditorTab_Plugin_1.PNG ├── IMessageEditorTab_Plugin_10.PNG ├── IMessageEditorTab_Plugin_11.PNG ├── IMessageEditorTab_Plugin_12.PNG ├── IMessageEditorTab_Plugin_13.PNG ├── IMessageEditorTab_Plugin_14.PNG ├── IMessageEditorTab_Plugin_15.PNG ├── IMessageEditorTab_Plugin_16.PNG ├── IMessageEditorTab_Plugin_17.PNG ├── IMessageEditorTab_Plugin_2.PNG ├── IMessageEditorTab_Plugin_3.PNG ├── IMessageEditorTab_Plugin_4.PNG ├── IMessageEditorTab_Plugin_5.PNG ├── IMessageEditorTab_Plugin_6.PNG ├── IMessageEditorTab_Plugin_7.PNG ├── IMessageEditorTab_Plugin_8.PNG ├── IMessageEditorTab_Plugin_9.PNG ├── JButton_Plugin_1.PNG ├── JButton_Plugin_2.PNG ├── JButton_Plugin_3.PNG ├── JButton_Plugin_4.PNG ├── JButton_Plugin_5.PNG ├── JButton_Plugin_6.PNG ├── JButton_Plugin_7.PNG ├── JButton_Plugin_8.PNG ├── JButton_Plugin_9.PNG ├── JsEditor1.PNG ├── JsEditor2.PNG ├── JsEditor3.PNG ├── JsEditor4.PNG ├── JsEditor5.PNG ├── Start1.png ├── Start2.png ├── Start3.png ├── Start4.png ├── Start5.png └── Start6.png ├── pom.xml ├── res ├── androidDefaultHooks.js ├── brida.js ├── bridaFunctions.js ├── bridaServicePyro.py └── iosDefaultHooks.js └── src └── main └── java └── burp ├── BridaButtonPlugin.java ├── BridaContextMenuPlugin.java ├── BridaHttpListenerPlugin.java ├── BridaMessageEditorPlugin.java ├── BridaThread.java ├── BurpExtender.java ├── CustomPlugin.java ├── CustomPluginsTableModel.java ├── DefaultHook.java ├── TrapTableItem.java ├── TrapTableModel.java └── TreePopup.java /BridaLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/BridaLogo.png -------------------------------------------------------------------------------- /BridaScreen1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/BridaScreen1.PNG -------------------------------------------------------------------------------- /Demo/Android/DemoAndroidBridaJS/brida.js: -------------------------------------------------------------------------------- 1 | const androidpinningwithca1 = require("./androidDefaultHooks.js").androidpinningwithca1 2 | const androidpinningwithoutca1 = require("./androidDefaultHooks.js").androidpinningwithoutca1 3 | const androidrooting1 = require("./androidDefaultHooks.js").androidrooting1 4 | const androidfingerprintbypass1 = require("./androidDefaultHooks.js").androidfingerprintbypass1 5 | const androidfingerprintbypass2hook = require("./androidDefaultHooks.js").androidfingerprintbypass2hook 6 | const androidfingerprintbypass2function = require("./androidDefaultHooks.js").androidfingerprintbypass2function 7 | const tracekeystore = require("./androidDefaultHooks.js").tracekeystore 8 | const listaliasesstatic = require("./androidDefaultHooks.js").listaliasesstatic 9 | const listaliasesruntime = require("./androidDefaultHooks.js").listaliasesruntime 10 | const dumpcryptostuff = require("./androidDefaultHooks.js").dumpcryptostuff 11 | const ios10pinning = require("./iosDefaultHooks.js").ios10pinning 12 | const ios11pinning = require("./iosDefaultHooks.js").ios11pinning 13 | const ios12pinning = require("./iosDefaultHooks.js").ios12pinning 14 | const ios13pinning = require("./iosDefaultHooks.js").ios13pinning 15 | const iosbypasstouchid = require("./iosDefaultHooks.js").iosbypasstouchid 16 | const iosjailbreak = require("./iosDefaultHooks.js").iosjailbreak 17 | const iosdumpkeychain = require("./iosDefaultHooks.js").iosdumpkeychain 18 | const iosdataprotectionkeys = require("./iosDefaultHooks.js").iosdataprotectionkeys 19 | const iosdumpcurrentencryptedapp = require("./iosDefaultHooks.js").iosdumpcurrentencryptedapp 20 | const dumpcryptostuffios = require("./iosDefaultHooks.js").dumpcryptostuffios 21 | const demangle = require("./iosDefaultHooks.js").demangle 22 | const getallclasses = require("./bridaFunctions").getallclasses 23 | const getallmodules = require("./bridaFunctions").getallmodules 24 | const getmoduleimports = require("./bridaFunctions").getmoduleimports 25 | const getmoduleexports = require("./bridaFunctions").getmoduleexports 26 | const getclassmethods = require("./bridaFunctions").getclassmethods 27 | const findobjcmethods = require("./bridaFunctions").findobjcmethods 28 | const findjavamethods = require("./bridaFunctions").findjavamethods 29 | const findimports = require("./bridaFunctions").findimports 30 | const findexports = require("./bridaFunctions").findexports 31 | const detachall = require("./bridaFunctions").detachall 32 | const trace = require("./bridaFunctions").trace 33 | const changereturnvalue = require("./bridaFunctions").changereturnvalue 34 | const getplatform = require("./bridaFunctions").getplatform 35 | 36 | // Brida User file: use this file to insert your Frida exports/hooks/functions. 37 | // Do not remove existing code (it is necessary for Brida) 38 | 39 | rpc.exports = { 40 | androidpinningwithca1, androidpinningwithoutca1, androidrooting1, 41 | androidfingerprintbypass1, androidfingerprintbypass2hook, 42 | androidfingerprintbypass2function, tracekeystore, listaliasesstatic, 43 | listaliasesruntime, dumpcryptostuff, 44 | ios10pinning, ios11pinning, ios12pinning, ios13pinning, 45 | iosbypasstouchid, iosjailbreak, iosdumpkeychain, iosdataprotectionkeys, 46 | iosdumpcurrentencryptedapp, dumpcryptostuffios, demangle, 47 | getallclasses, getallmodules, getmoduleimports, getmoduleexports, 48 | getclassmethods, findobjcmethods, findjavamethods, findimports, 49 | findexports, detachall, trace, changereturnvalue, getplatform, 50 | 51 | // BE CAREFUL: Do not use uppercase characters in exported function name (automatically converted lowercase by Pyro) 52 | mydecrypt: function(message) { 53 | var ret = null; 54 | Java.perform(function () { 55 | var mioCripto = Java.use("com.dombroks.android_flask.mioCripto"); 56 | ret = mioCripto.decrypt(message); 57 | console.log("PLUGIN DECRYPT"); 58 | }); 59 | return ret; 60 | }, 61 | 62 | myencrypt: function(message) { 63 | var ret = null; 64 | Java.perform(function () { 65 | var mioCripto = Java.use("com.dombroks.android_flask.mioCripto"); 66 | ret = mioCripto.encrypt(message); 67 | console.log("PLUGIN ENCRYPT"); 68 | }); 69 | return ret; 70 | } 71 | 72 | } 73 | 74 | // Put here you Frida hooks! 75 | 76 | if(Java.available) { 77 | 78 | Java.perform(function() { 79 | 80 | var HostnameVerifierInterface = Java.use('javax.net.ssl.HostnameVerifier') 81 | const MyHostnameVerifier = Java.registerClass({ 82 | name: 'org.dummyPackage.MyHostnameVerifier', 83 | implements: [HostnameVerifierInterface], 84 | methods: { 85 | verify: [{ 86 | returnType: 'boolean', 87 | argumentTypes: ['java.lang.String', 'javax.net.ssl.SSLSession'], 88 | implementation(hostname, session) { 89 | console.log('[+] Hostname verification bypass'); 90 | return true; 91 | } 92 | }], 93 | } 94 | }); 95 | 96 | var hostnameVerifierRef = Java.use('okhttp3.OkHttpClient')['hostnameVerifier'].overload(); 97 | hostnameVerifierRef.implementation = function() { 98 | return MyHostnameVerifier.$new(); 99 | } 100 | console.log("[+] Hostname verifier replaced") 101 | 102 | }); 103 | 104 | } 105 | 106 | 107 | 108 | // Auxiliary functions - You can remove them if you don't need them! 109 | 110 | // Convert a hex string to a byte array 111 | function hexToBytes(hex) { 112 | for (var bytes = [], c = 0; c < hex.length; c += 2) 113 | bytes.push(parseInt(hex.substr(c, 2), 16)); 114 | return bytes; 115 | } 116 | 117 | // Convert a ASCII string to a hex string 118 | function stringToHex(str) { 119 | return str.split("").map(function(c) { 120 | return ("0" + c.charCodeAt(0).toString(16)).slice(-2); 121 | }).join(""); 122 | } 123 | 124 | // Convert a hex string to a ASCII string 125 | function hexToString(hexStr) { 126 | var hex = hexStr.toString();//force conversion 127 | var str = ''; 128 | for (var i = 0; i < hex.length; i += 2) 129 | str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); 130 | return str; 131 | } 132 | 133 | // Convert a byte array to a hex string 134 | function bytesToHex(bytes) { 135 | for (var hex = [], i = 0; i < bytes.length; i++) { 136 | hex.push((bytes[i] >>> 4).toString(16)); 137 | hex.push((bytes[i] & 0xF).toString(16)); 138 | } 139 | return hex.join(""); 140 | } 141 | 142 | // Native ArrayBuffer to Base64 143 | // https://gist.github.com/jonleighton/958841 144 | function base64ArrayBuffer(arrayBuffer) { 145 | var base64 = '' 146 | var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 147 | 148 | var bytes = new Uint8Array(arrayBuffer) 149 | var byteLength = bytes.byteLength 150 | var byteRemainder = byteLength % 3 151 | var mainLength = byteLength - byteRemainder 152 | 153 | var a, b, c, d 154 | var chunk 155 | 156 | // Main loop deals with bytes in chunks of 3 157 | for (var i = 0; i < mainLength; i = i + 3) { 158 | // Combine the three bytes into a single integer 159 | chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2] 160 | 161 | // Use bitmasks to extract 6-bit segments from the triplet 162 | a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18 163 | b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12 164 | c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6 165 | d = chunk & 63 // 63 = 2^6 - 1 166 | 167 | // Convert the raw binary segments to the appropriate ASCII encoding 168 | base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d] 169 | } 170 | 171 | // Deal with the remaining bytes and padding 172 | if (byteRemainder == 1) { 173 | chunk = bytes[mainLength] 174 | 175 | a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2 176 | 177 | // Set the 4 least significant bits to zero 178 | b = (chunk & 3) << 4 // 3 = 2^2 - 1 179 | 180 | base64 += encodings[a] + encodings[b] + '==' 181 | } else if (byteRemainder == 2) { 182 | chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1] 183 | 184 | a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10 185 | b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4 186 | 187 | // Set the 2 least significant bits to zero 188 | c = (chunk & 15) << 2 // 15 = 2^4 - 1 189 | 190 | base64 += encodings[a] + encodings[b] + encodings[c] + '=' 191 | } 192 | 193 | return base64 194 | } 195 | -------------------------------------------------------------------------------- /Demo/Android/DemoAndroidBridaJS/bridaFunctions.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getallclasses, getallmodules, getmoduleimports, getmoduleexports, 3 | getclassmethods, findobjcmethods, findjavamethods, findimports, 4 | findexports, detachall, trace, changereturnvalue, getplatform 5 | } 6 | 7 | function getallclasses() { 8 | var result = [] 9 | if (ObjC.available) { 10 | for (var className in ObjC.classes) { 11 | if (ObjC.classes.hasOwnProperty(className)) { 12 | result.push(className); 13 | } 14 | } 15 | } else if(Java.available) { 16 | Java.perform(function() { 17 | Java.enumerateLoadedClasses({ 18 | onMatch: function (className) { 19 | result.push(className); 20 | }, 21 | onComplete: function() { 22 | } 23 | }); 24 | }); 25 | } 26 | return result; 27 | } 28 | 29 | function getallmodules() { 30 | var results = {} 31 | var matches = Process.enumerateModules( { 32 | onMatch: function (module) { 33 | results[module['name']] = module['base']; 34 | }, 35 | onComplete: function () { 36 | } 37 | }); 38 | return results; 39 | } 40 | 41 | function getmoduleimports(importname) { 42 | var results = {} 43 | var matches = Module.enumerateImports(importname, { 44 | onMatch: function (module) { 45 | results[module['type'] + ": " + module['name']] = module['address']; 46 | }, 47 | onComplete: function () { 48 | } 49 | }); 50 | return results; 51 | } 52 | 53 | function getmoduleexports(exportname) { 54 | var results = {} 55 | var matches = Module.enumerateExports(exportname, { 56 | onMatch: function (module) { 57 | results[module['type'] + ": " + module['name']] = module['address']; 58 | }, 59 | onComplete: function () { 60 | } 61 | }); 62 | return results; 63 | } 64 | 65 | function getclassmethods(classname) { 66 | var results = {} 67 | if (ObjC.available) { 68 | var resolver = new ApiResolver("objc"); 69 | var matches = resolver.enumerateMatches("*[" + classname + " *]", { 70 | onMatch: function (match) { 71 | results[match['name']] = match['address']; 72 | }, 73 | onComplete: function () { 74 | } 75 | }); 76 | } else if(Java.available) { 77 | Java.perform(function() { 78 | results = getJavaMethodArgumentTypes(classname); 79 | }); 80 | } 81 | return results; 82 | } 83 | 84 | function findjavamethods(searchstring) { 85 | var results = {} 86 | if(Java.available) { 87 | Java.perform(function() { 88 | var groups = [] 89 | groups.push(Java.enumerateMethods('*' + searchstring + '*!*/s')) 90 | groups.push(Java.enumerateMethods('*!*' + searchstring + '*/s')) 91 | groups.forEach(g => { 92 | g.forEach(classLoader => { 93 | classLoader.classes.forEach(c => { 94 | var className = c.name; 95 | c.methods.forEach(m => { 96 | var methodSignature = className + "!" + m; 97 | results[methodSignature] = null; 98 | }); 99 | }); 100 | }); 101 | }); 102 | }); 103 | } 104 | return results; 105 | } 106 | 107 | 108 | 109 | function findobjcmethods(searchstring) { 110 | var results = {} 111 | var resolver = new ApiResolver("objc"); 112 | var matches = resolver.enumerateMatches("*[*" + searchstring + "* *]", { 113 | onMatch: function (match) { 114 | results[match['name']] = match['address']; 115 | }, 116 | onComplete: function () { 117 | } 118 | }); 119 | matches = resolver.enumerateMatches("*[* *" + searchstring + "*]", { 120 | onMatch: function (match) { 121 | results[match['name']] = match['address']; 122 | }, 123 | onComplete: function () { 124 | } 125 | }); 126 | return results; 127 | } 128 | 129 | function findimports(searchstring) { 130 | var results = {} 131 | var resolver = new ApiResolver("module"); 132 | var matches = resolver.enumerateMatches("imports:*" + searchstring + "*!*", { 133 | onMatch: function (match) { 134 | results[match['name']] = match['address']; 135 | }, 136 | onComplete: function () { 137 | } 138 | }); 139 | matches = resolver.enumerateMatches("imports:*!*" + searchstring + "*", { 140 | onMatch: function (match) { 141 | results[match['name']] = match['address']; 142 | }, 143 | onComplete: function () { 144 | } 145 | }); 146 | return results; 147 | } 148 | 149 | function findexports(searchstring) { 150 | var results = {} 151 | var resolver = new ApiResolver("module"); 152 | var matches = resolver.enumerateMatches("exports:*" + searchstring + "*!*", { 153 | onMatch: function (match) { 154 | results[match['name']] = match['address']; 155 | }, 156 | onComplete: function () { 157 | } 158 | }); 159 | matches = resolver.enumerateMatches("exports:*!*" + searchstring + "*", { 160 | onMatch: function (match) { 161 | results[match['name']] = match['address']; 162 | }, 163 | onComplete: function () { 164 | } 165 | }); 166 | return results; 167 | } 168 | 169 | function detachall() { 170 | Interceptor.detachAll(); 171 | } 172 | 173 | // generic trace 174 | function trace(pattern,type,backtrace) { 175 | // SINGLE EXPORT (ALL EXPORT OF A MODULE CAN BE A MESS AND CRASH THE APP) 176 | if(type == "export") { 177 | var res = new ApiResolver("module"); 178 | pattern = "exports:" + pattern; 179 | var matches = res.enumerateMatchesSync(pattern); 180 | var targets = uniqBy(matches, JSON.stringify); 181 | targets.forEach(function(target) { 182 | traceModule(target.address, target.name, backtrace); 183 | }); 184 | //OBJC 185 | } else if(type.startsWith("objc")) { 186 | if (ObjC.available) { 187 | var res; 188 | if(type === "objc_class") { 189 | res = new ApiResolver("objc"); 190 | pattern = "*[" + pattern + " *]"; 191 | } else if(type === "objc_method") { 192 | res = new ApiResolver("objc"); 193 | } 194 | var matches = res.enumerateMatchesSync(pattern); 195 | var targets = uniqBy(matches, JSON.stringify); 196 | targets.forEach(function(target) { 197 | traceObjC(target.address, target.name,backtrace); 198 | }); 199 | } 200 | // ANDROID 201 | } else if(type.startsWith("java")) { 202 | if(Java.available) { 203 | Java.perform(function() { 204 | if(type === "java_class") { 205 | var methodsDictionary = getJavaMethodArgumentTypes(pattern); 206 | var targets = Object.keys(methodsDictionary); 207 | targets.forEach(function(targetMethod) { 208 | traceJavaMethod(targetMethod,backtrace); 209 | }); 210 | } else { 211 | traceJavaMethod(pattern,backtrace); 212 | } 213 | }); 214 | } 215 | } 216 | } 217 | 218 | function changereturnvalue(pattern, type, typeret, newret) { 219 | if(ObjC.available) { 220 | changeReturnValueIOS(pattern, type, typeret, newret); 221 | } else if(Java.available) { 222 | Java.perform(function() { 223 | changeReturnValueAndroid(pattern, type, typeret, newret); 224 | }); 225 | } else { 226 | changeReturnValueGeneric(pattern, type, typeret, newret); 227 | } 228 | } 229 | 230 | function getplatform() { 231 | 232 | if(Java.available) { 233 | return 0; 234 | } else if(ObjC.available){ 235 | return 1; 236 | } else { 237 | return 2; 238 | } 239 | 240 | } 241 | 242 | 243 | /* 244 | This method is used to get Java methods with arguments in bytecode syntex. By simply calling the getDeclaredMethods of a Java Class object 245 | and then calling toString on each Method object we do not get types in bytecode format. For example we get 'byte[]' instead of 246 | '[B'. This function uses overload object of frida to get types in correct bytecode form. 247 | */ 248 | function getJavaMethodArgumentTypes(classname) { 249 | if(Java.available) { 250 | var results = {}; 251 | Java.perform(function() { 252 | var hook = Java.use(classname); 253 | var res = hook.class.getDeclaredMethods(); 254 | res.forEach(function(s) { 255 | //console.log("s " + s); 256 | var targetClassMethod = parseJavaMethod(s.toString()); 257 | //console.log("targetClassMethod " + targetClassMethod); 258 | var delim = targetClassMethod.lastIndexOf("."); 259 | if (delim === -1) return; 260 | var targetClass = targetClassMethod.slice(0, delim) 261 | var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length) 262 | //console.log("targetClass " + targetClass); 263 | //console.log("targetMethod " + targetMethod); 264 | var hookClass = Java.use(targetClass); 265 | var classMethodOverloads = hookClass[targetMethod].overloads; 266 | classMethodOverloads.forEach(function(cmo) { 267 | // overload.argumentTypes is an array of objects representing the arguments. In the "className" field of each object there 268 | // is the bytecode form of the class of the current argument 269 | var argumentTypes = cmo.argumentTypes; 270 | var argumentTypesArray = [] 271 | argumentTypes.forEach(function(cmo) { 272 | argumentTypesArray.push(cmo.className); 273 | }); 274 | var argumentTypesString = argumentTypesArray.toString(); 275 | // overload.returnType.className contain the bytecode form of the class of the return value 276 | var currentReturnType = cmo.returnType.className; 277 | var newPattern = currentReturnType + " " + targetClassMethod + "(" + argumentTypesString + ")"; 278 | //console.log(newPattern); 279 | results[newPattern] = 0; 280 | }); 281 | hookClass.$dispose; 282 | }); 283 | hook.$dispose; 284 | }); 285 | return results; 286 | } 287 | } 288 | 289 | function changeReturnValueIOS(pattern, type, typeret, newret) { 290 | var res; 291 | if(type === "objc_method") { 292 | res = new ApiResolver("objc"); 293 | } else { 294 | // SINGLE EXPORT 295 | res = new ApiResolver("module"); 296 | pattern = "exports:" + pattern; 297 | } 298 | var matches = res.enumerateMatchesSync(pattern); 299 | var targets = uniqBy(matches, JSON.stringify); 300 | targets.forEach(function(target) { 301 | Interceptor.attach(target.address, { 302 | onEnter: function(args) { 303 | }, 304 | onLeave: function(retval) { 305 | if(typeret === "String") { 306 | var a1 = ObjC.classes.NSString.stringWithString_(newret); 307 | try { 308 | console.log("*** " + pattern + " Replacing " + ObjC.Object(retval) + " with " + a1); 309 | } catch(err) { 310 | console.log("*** " + pattern + " Replacing " + retval + " with " + a1); 311 | } 312 | retval.replace(a1); 313 | } else if(typeret === "Ptr") { 314 | console.log("*** " + pattern + " Replacing " + ptr(retval) + " with " + ptr(newret)); 315 | retval.replace(ptr(newret)); 316 | } else if(typeret === "Boolean") { 317 | if(newret === "true") { 318 | var toRet = 1; 319 | } else { 320 | var toRet = 0; 321 | } 322 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 323 | retval.replace(toRet); 324 | } else { 325 | console.log("*** " + pattern + " Replacing " + retval + " with " + newret); 326 | retval.replace(newret); 327 | } 328 | } 329 | }); 330 | }); 331 | console.log("*** Replacing return value of " + pattern + " with " + newret); 332 | } 333 | 334 | function changeReturnValueGeneric(pattern, type, typeret, newret) { 335 | var res = new ApiResolver("module"); 336 | pattern = "exports:" + pattern; 337 | var matches = res.enumerateMatchesSync(pattern); 338 | var targets = uniqBy(matches, JSON.stringify); 339 | targets.forEach(function(target) { 340 | Interceptor.attach(target.address, { 341 | onEnter: function(args) { 342 | }, 343 | onLeave: function(retval) { 344 | if(typeret === "Ptr") { 345 | console.log("*** " + pattern + " Replacing " + ptr(retval) + " with " + ptr(newret)); 346 | retval.replace(ptr(newret)); 347 | } else if(typeret === "Boolean") { 348 | if(newret === "true") { 349 | var toRet = 1; 350 | } else { 351 | var toRet = 0; 352 | } 353 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 354 | retval.replace(toRet); 355 | } else { 356 | console.log("*** " + pattern + " Replacing " + retval + " with " + newret); 357 | retval.replace(newret); 358 | } 359 | } 360 | }); 361 | }); 362 | console.log("*** Replacing return value of " + pattern + " with " + newret); 363 | } 364 | 365 | function changeReturnValueAndroid(pattern, type, typeret, newret) { 366 | if(type === "java_method") { 367 | var targetClassMethod = parseJavaMethod(pattern); 368 | //console.log(targetClassMethod); 369 | var argsTargetClassMethod = getJavaMethodArguments(pattern); 370 | //console.log(argsTargetClassMethod); 371 | var delim = targetClassMethod.lastIndexOf("."); 372 | if (delim === -1) return; 373 | var targetClass = targetClassMethod.slice(0, delim) 374 | var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length) 375 | //console.log(targetClass); 376 | //console.log(targetMethod); 377 | var hook = Java.use(targetClass); 378 | hook[targetMethod].overload.apply(hook[targetMethod],argsTargetClassMethod).implementation = function() { 379 | var retval = this[targetMethod].apply(this, arguments); 380 | var toRet = newret; 381 | if(typeret === "String") { 382 | var stringClass = Java.use("java.lang.String"); 383 | toRet = stringClass.$new(newret); 384 | } else if(typeret === "Ptr") { 385 | toRet = ptr(newret); 386 | } else if(typeret === "Boolean") { 387 | if(newret === "true") { 388 | toRet = true; 389 | } else { 390 | toRet = false; 391 | } 392 | } 393 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 394 | return toRet; 395 | } 396 | // SINGLE EXPORT 397 | } else { 398 | var res = new ApiResolver("module"); 399 | var pattern = "exports:" + pattern; 400 | var matches = res.enumerateMatchesSync(pattern); 401 | var targets = uniqBy(matches, JSON.stringify); 402 | targets.forEach(function(target) { 403 | Interceptor.attach(target.address, { 404 | onEnter: function(args) { 405 | }, 406 | onLeave: function(retval) { 407 | var toRet = newret; 408 | if(typeret === "String") { 409 | var stringClass = Java.use("java.lang.String"); 410 | var toRet = stringClass.$new(newret); 411 | } else if(typeret === "ptr") { 412 | toRet = ptr(newret); 413 | } else if(typeret === "Boolean") { 414 | if(newret === "true") { 415 | var toRet = 1; 416 | } else { 417 | var toRet = 0; 418 | } 419 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 420 | retval.replace(toRet); 421 | } 422 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 423 | retval.replace(toRet); 424 | } 425 | }); 426 | }); 427 | } 428 | console.log("*** Replacing return value of " + pattern + " with " + newret); 429 | } 430 | 431 | // trace ObjC methods 432 | function traceObjC(impl, name, backtrace) { 433 | console.log("*** Tracing " + name); 434 | Interceptor.attach(impl, { 435 | onEnter: function(args) { 436 | console.log("*** entered " + name); 437 | console.log("Caller: " + DebugSymbol.fromAddress(this.returnAddress)); 438 | // print args 439 | if (name.indexOf(":") !== -1) { 440 | console.log("Parameters:"); 441 | var par = name.split(":"); 442 | par[0] = par[0].split(" ")[1]; 443 | for (var i = 0; i < par.length - 1; i++) { 444 | printArg(par[i] + ": ", args[i + 2]); 445 | } 446 | } 447 | if(backtrace === "true") { 448 | console.log("Backtrace:\n\t" + Thread.backtrace(this.context, Backtracer.ACCURATE) 449 | .map(DebugSymbol.fromAddress).join("\n\t")); 450 | } 451 | }, 452 | onLeave: function(retval) { 453 | console.log("*** exiting " + name); 454 | console.log("Return value:"); 455 | printArg("retval: ", retval); 456 | } 457 | }); 458 | } 459 | 460 | 461 | 462 | // trace a specific Java Method 463 | function traceJavaMethod(pattern,backtrace) { 464 | var targetClassMethod = parseJavaMethod(pattern); 465 | //console.log(targetClassMethod); 466 | var argsTargetClassMethod = getJavaMethodArguments(pattern); 467 | //console.log(argsTargetClassMethod); 468 | var delim = targetClassMethod.lastIndexOf("."); 469 | if (delim === -1) return; 470 | var targetClass = targetClassMethod.slice(0, delim) 471 | var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length) 472 | var hook = Java.use(targetClass); 473 | //var overloadCount = hook[targetMethod].overloads.length; 474 | console.log("*** Tracing " + pattern); 475 | hook[targetMethod].overload.apply(hook[targetMethod],argsTargetClassMethod).implementation = function() { 476 | console.log("*** entered " + targetClassMethod); 477 | // print args 478 | if (arguments.length) console.log("Parameters:"); 479 | for (var j = 0; j < arguments.length; j++) { 480 | console.log("\targ[" + j + "]: " + arguments[j]); 481 | } 482 | // print backtrace 483 | if(backtrace === "true") { 484 | Java.perform(function() { 485 | var threadClass = Java.use("java.lang.Thread"); 486 | var currentThread = threadClass.currentThread(); 487 | var currentStackTrace = currentThread.getStackTrace(); 488 | console.log("Backtrace:"); 489 | currentStackTrace.forEach(function(st) { 490 | console.log("\t" + st.toString()); 491 | }); 492 | }); 493 | } 494 | // print retval 495 | var retval = this[targetMethod].apply(this, arguments); 496 | console.log("*** exiting " + targetClassMethod); 497 | console.log("Return value:"); 498 | console.log("\tretval: " + retval); 499 | return retval; 500 | } 501 | } 502 | 503 | // trace Module functions 504 | function traceModule(impl, name, backtrace) { 505 | console.log("*** Tracing " + name); 506 | Interceptor.attach(impl, { 507 | onEnter: function(args) { 508 | console.log("*** entered " + name); 509 | if(backtrace === "true") { 510 | console.log("Backtrace:\n\t" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n\t")); 511 | } 512 | }, 513 | onLeave: function(retval) { 514 | console.log("*** exiting " + name); 515 | console.log("Return value:"); 516 | if(ObjC.available) { 517 | printArg("retval: ", retval); 518 | } else { 519 | console.log("\tretval: ", retval); 520 | } 521 | } 522 | }); 523 | } 524 | 525 | // print helper 526 | function printArg(desc, arg) { 527 | if(arg != 0x0) { 528 | try { 529 | var objectArg = ObjC.Object(arg); 530 | console.log("\t(" + objectArg.$className + ") " + desc + objectArg.toString()); 531 | } catch(err2) { 532 | console.log("\t" + desc + arg); 533 | } 534 | } else { 535 | console.log("\t" + desc + "0x0"); 536 | } 537 | } 538 | 539 | // remove duplicates from array 540 | function uniqBy(array, key) { 541 | var seen = {}; 542 | return array.filter(function(item) { 543 | var k = key(item); 544 | return seen.hasOwnProperty(k) ? false : (seen[k] = true); 545 | }); 546 | } 547 | 548 | /* 549 | INPUT LIKE: public boolean a.b.functionName(java.lang.String) 550 | OUTPUT LIKE: a.b.functionName 551 | */ 552 | function parseJavaMethod(method) { 553 | var parSplit = method.split("("); 554 | var spaceSplit = parSplit[0].split(" "); 555 | return spaceSplit[spaceSplit.length - 1]; 556 | } 557 | 558 | //INPUT LIKE: public boolean a.b.functionName(java.lang.String,java.lang.String) 559 | //OUTPUT LIKE: ["java.lang.String","java.lang.String"] 560 | function getJavaMethodArguments(method) { 561 | var m = method.match(/.*\((.*)\).*/); 562 | if(m[1] !== "") { 563 | return m[1].split(","); 564 | } else { 565 | return []; 566 | } 567 | } 568 | 569 | -------------------------------------------------------------------------------- /Demo/Android/HIP2021_demo.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/Demo/Android/HIP2021_demo.apk -------------------------------------------------------------------------------- /Demo/Android/README.md: -------------------------------------------------------------------------------- 1 | # Android Demo 2 | 3 | In this folder there is the Android demo application used to show Brida features during **Hack In Paris 2021** conference. 4 | 5 | The backend is a simple Flask Python application. 6 | 7 | The application implements **TLS Pinning** and **Hostname validation**, that can be bypassed using Brida scripts. TLS Pinning can be bypassed using the two pinning bypass scripts that can be enabled in the "Hooks and functions" tab. Hostname validation script has been directly included in the Brida code supplied with the demo (it can also be enabled in the "Hooks and functions" tab present in the last Git Brida code). 8 | 9 | ## Run the Flask backend 10 | 11 | 1. Install dependencies (pycypto should be installed **before** pycryptodome, otherwise exceptions can occur) 12 | 13 | ``` 14 | pip install flask 15 | pip install pycrypto 16 | pip install pycryptodome 17 | ``` 18 | 19 | 2. Run the server 20 | 21 | ``` 22 | export FLASK_APP=server 23 | export FLASK_ENV=development 24 | flask run --cert=certs/server-cert.pem --key=certs/server-key.pem -h -p 25 | ``` 26 | 27 | ## Run the application 28 | 29 | 1. Install APK 30 | 2. Run the application 31 | 3. Set the URL of the backend in the upper part 32 | 33 | **Note**: it is necessary to bypass pinning and hostname validation in order to be able to use the application. If you want to try the application without Burp Suite in the middle and without bypassing the pinning read the paragraph "Make the application work without bypassing the pinning and without Burp Suite in the middle" (**not** necessary to use the demo). 34 | 35 | ## Load Brida plugins 36 | 37 | 1. Configure Brida (refer to the [documentation](https://github.com/federicodotta/Brida/wiki/Start) for details). The application ID of the demo is **com.dombroks.android_flask** 38 | 2. Click on the "Select folder" button of the "Frida JS files folder" and select the supplied "DemoAndroidFridaJS" folder 39 | 3. Enable **one of** the Brida pinning bypass in the "Hooks and functions" -> "Android" section 40 | 4. Load the plugins, by clicking on the button "Import plugins" in the "Custom plugins" tab and choosing the supplied "exportedPluginsDemoAndroid.csv" file 41 | 5. Enable the plugin(s) you want to try using the corresponding "Enable" button in the same tab 42 | 6. Spawn/attach the application 43 | 44 | ## Supplied Brida plugins 45 | 46 | 1. **Decrypt_context**: it adds an entry to the context menu that decrypts the higlighted value using Brida, replacing it with its decrypted form, if possible. If the highlighted value is in a non-editable pane, a pop-up appears 47 | 2. **Encrypt_context**: it adds an entry to the context menu that encrypts the higlighted value using Brida, replacing it with its encrypted form, if possible. If the highlighted value is in a non-editable pane, a pop-up appears 48 | 3. **Decrypt_messageEditorTab**: it adds a message editor tab to HTTP requests and responses, that shows the decrypted form of the body of the current HTTP message. If the tab is editable and the decrypted value is modified, Brida replace the original body with a new one containing the encrypted modified body 49 | 4. **EncryptRequest_IHttpListener**: when it is enabled, it encrypts transparently the bodies of all requests generated from the Scanner and the Intruder Burp Suite tools (but it can be edited to add other Burp Suite tools). It can be used by sending a request to the Intruder/Scanner **with the body already decrypted**. In this way Burp Suite can adds his payloads and the body will be transparently encrypted by Brida before the transmission to the backend 50 | 5. **DecryptResponse_IHttpListener**: when it is enabled, it decrypts transparently the bodies of all responses received by the Scanner and the Intruder Burp Suite tools (but it can be edited to add other Burp Suite tools). It can be used in conjunction with the previous plugin to have the body of the HTTP responses decrypted in the Scanner and in the Intruder tools, allowing Burp Suite or the pentester to understand if the attack vector succeeded in an easy way 51 | 6. **Decrypt_button**: it adds a button to the "Hooks and functions" -> "Android" section that decrypt the supplied input 52 | 7. **Encrypt_button**: it adds a button to the "Hooks and functions" -> "Android" section that encrypt the supplied input 53 | 54 | ## Make the application work without bypassing the pinning and without Burp Suite in the middle 55 | 56 | If you really want to try the demo application without bypassing the pinning (and consequently also without Burp Suite in the middle), you can install the CA we used to sign the server certificate and add "demo.hnsecfakedomain.it" to the /etc/hosts file of the device (because otherwise TLS hostname check will fail). 57 | 58 | **Note**: it is **not** necessary to execute this procedure to run the demo. Simply run the application and use Brida or something else to bypass TLS checks! ;) 59 | 60 | 1. Install the supplied CA certificate (*certs/9e487db2.0* is the DER convertion of *certs/ca-cert.pem* with the correct name for installing in Android with the following procedure) in your device (**PAY ATTENTION: remove that CA certificate after using the demo**) 61 | 62 | This procedure can be executed in many ways. One is the following one. **Pay attention if you don't understand what you are doing because you may damage your device!** 63 | 64 | ``` 65 | adb push 9e487db2.0 /data/local/tmp 66 | adb shell 67 | ``` 68 | 69 | From the device: 70 | 71 | ``` 72 | su 73 | mount -o rw,remount /system 74 | cp /data/local/tmp/9e487db2.0 /system/etc/security/cacerts/ 75 | chmod 644 /system/etc/security/cacerts/9e487db2.0 76 | chown root:root /system/etc/security/cacerts/9e487db2.0 77 | sync 78 | mount -o ro,remount /system 79 | reboot 80 | ``` 81 | 82 | 2. Add demo.hnsecfakedomain.it to the /etc/hosts file of your Android device (necessary, otherwise hostname TLS checks will fail) 83 | 84 | ``` 85 | su 86 | mount -o rw,remount /system 87 | cat >> /system/etc/hosts 88 | ``` 89 | 90 | Now type and entry of the hosts that points to demo.hnsecfakedomain.it (es. 192.168.12.1 demo.hnsecfakedomain.it) and then exit with CTRL+D. Then: 91 | 92 | ``` 93 | sync 94 | mount -o ro,remount /system 95 | ``` 96 | 97 | 3. Set the URL of the backend in the upper part of the application, using the hostname demo.hnsecfakedomain.it, that now points to your backend server, and the port in which the server is listening 98 | 99 | 4. After you used the demo, the certificate can be removed **from the device** as follows: 100 | 101 | ``` 102 | su 103 | mount -o rw,remount /system 104 | rm /system/etc/security/cacerts/9e487db2.0 105 | sync 106 | mount -o ro,remount /system 107 | reboot 108 | ``` 109 | 110 | ## Disclaimer 111 | 112 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Demo/Android/certs/9e487db2.0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/Demo/Android/certs/9e487db2.0 -------------------------------------------------------------------------------- /Demo/Android/certs/ca-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIF7zCCA9egAwIBAgIUVOR2kvWGY22ZZsQXwT8cBk35upYwDQYJKoZIhvcNAQEL 3 | BQAwgYYxCzAJBgNVBAYTAklUMQ0wCwYDVQQIDARSb21lMQ0wCwYDVQQHDARSb21l 4 | MRQwEgYDVQQKDAtITiBTZWN1cml0eTELMAkGA1UECwwCUFQxDjAMBgNVBAMMBWhu 5 | c2VjMSYwJAYJKoZIhvcNAQkBFhd0ZXN0QGhuc2VjZmFrZWRvbWFpbi5pdDAeFw0y 6 | MTExMjMxODE4NDlaFw0yMjExMjMxODE4NDlaMIGGMQswCQYDVQQGEwJJVDENMAsG 7 | A1UECAwEUm9tZTENMAsGA1UEBwwEUm9tZTEUMBIGA1UECgwLSE4gU2VjdXJpdHkx 8 | CzAJBgNVBAsMAlBUMQ4wDAYDVQQDDAVobnNlYzEmMCQGCSqGSIb3DQEJARYXdGVz 9 | dEBobnNlY2Zha2Vkb21haW4uaXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK 10 | AoICAQCU/6o5+bqOAHDy6Cg+qxAj5+8Y3T0cexedrxIycugjVGyYhHafCN39R0Qw 11 | KjctEN0iSJGxq6S7uPkdAuMFe0ow5HLNRLzQ2cP+I8nrZfaCLV0t+xkK9Z7Pkn1r 12 | zujDaJQB7tnuMwwkkOPmav8cdWgUZIYfMkFGuBcPreVFiW9b6jGhzGBpN6JgWQLm 13 | 3WhXKf7KQ+2R/4d7rkXWuHE9eRmFiFMF79nUYqcHJzIA3iYLiz/db+y1fmdKuBne 14 | fzUm15Yo40/lvP+4BkGh4maJ1VsUlArfouFhRTzIVnZJSxzB/IHE+CanoCx1Nkqh 15 | xyV0MsLYbSfRZ1kbrno/3yz1/zbZd7Y44XeCC6fSG11fgnQt/Tcf/zPJ1J2pFJ1v 16 | oorUyxe3tyB8MIDcswrBGafTmYtM5llarex65xkKFEIAxGMQyGFZjIuW0VDME+DM 17 | y19YeWVZqbRBTst5bRuAeQsyZyDjkeODY/ySUoBvgPHvLR9BudJJtIGswogQZ0wd 18 | oSnBz+evt4Z8wXFqDwG4Z06i1M7O4LjjyDZPw922AOYwEx5UJ5b6hZ9tdRrsrwKE 19 | nVftJgmqLpe6VbKXu5H00oQ3Ztq+ykSOM6DkG5avEs6ZnRc08vEwrzTNekH6eGtw 20 | B35rcFO0P1ZllkQDyJBPIVOfgt5DfJIUpGlnkXKLuMyPBKjy4QIDAQABo1MwUTAd 21 | BgNVHQ4EFgQUXJrZKFHDhmaNvAeS9jOgLib6frgwHwYDVR0jBBgwFoAUXJrZKFHD 22 | hmaNvAeS9jOgLib6frgwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC 23 | AgEATZWqoWU4+GyUzBR5JFzSt8zdpAw9nK7EvRkXJ6P9YiqzE7dIXr05YzFsGQbn 24 | b/J2w+uiBgdcdnuSk3pVvuO/vCFTpmHCeX6prk+kqRarWsnGzVIRL78OePrV+mzy 25 | n0n2NUqjTf6QCioII+egR+K85iRkw30Fjpv/mrl+M9qF7QPRHIhi1ksJ+KFwQjQv 26 | dkh7277IFd2/J/Bj/szR01iyXuhbjgla6ptU+OeL7XLO3bKH2p+GKRSM7LvIl+1O 27 | cZLZp2TQ6Hyud6NGznOT0FxPJf2M4S/rNa9pGXo/Ri76MMn13o9Nfxx91kJzHtSc 28 | 9EhhHq9LjzWnTpiKey7XSb9e8uPUC5XZLbH/LbehUdKtlYBxGQZMv9TExAFTFWj6 29 | tP243MsI9+IKDzdhM5OTCU29xyTwH+3l+mpslT+qyXXPIIZ9ZIvUdPQWImh6Ii3R 30 | zd+gaPLof577K5YUnyZlVsLMu288A9yYolb3wBYoluPQwbmaRHw5Bzxv9jFVlWTl 31 | ObncpcQNAaN4wxrsJyYpFb3XX95AZDCJ7xXtHtDNiD2frq6zf298c6SDKHk+E1oa 32 | MXNbsJ0lNJ+43xfJqONWMhlq7++R69IGkjuIR5dJANyzxkK3O6aqA0Yzvmak19iD 33 | fej//5CvIsf0Zq3NPHMkvX3x1omqe6M641t9rMOxkFz1CW4= 34 | -----END CERTIFICATE----- 35 | -------------------------------------------------------------------------------- /Demo/Android/certs/server-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFwDCCA6igAwIBAgIUEoEwc5ry0riNAER8ZG46LsfR1M0wDQYJKoZIhvcNAQEL 3 | BQAwgYYxCzAJBgNVBAYTAklUMQ0wCwYDVQQIDARSb21lMQ0wCwYDVQQHDARSb21l 4 | MRQwEgYDVQQKDAtITiBTZWN1cml0eTELMAkGA1UECwwCUFQxDjAMBgNVBAMMBWhu 5 | c2VjMSYwJAYJKoZIhvcNAQkBFhd0ZXN0QGhuc2VjZmFrZWRvbWFpbi5pdDAeFw0y 6 | MTExMjMxODE5MDdaFw0yMjExMjMxODE5MDdaMGMxCzAJBgNVBAYTAklUMQ0wCwYD 7 | VQQIDARSb21lMQ0wCwYDVQQHDARSb21lMRQwEgYDVQQKDAtITiBTZWN1cml0eTEg 8 | MB4GA1UEAwwXZGVtby5obnNlY2Zha2Vkb21haW4uaXQwggIiMA0GCSqGSIb3DQEB 9 | AQUAA4ICDwAwggIKAoICAQClXtRH6XX2AswIAoRkuNc1FHRnRYeZqkEs+fkEkVhO 10 | c0lqLj5WhXKXH5A594iJnf/tyX/73Y3gYtevK8+AD9slbq2w5uX8g1qxSRpJ55im 11 | a775lztV4AmNC96Kk9D6V6yEN3E8Xdb8aAgSXMM1ervJXW/rAZ5FrtzjmsEn90mm 12 | PAMI0DbEIErZhDuJ1aiGUig0etSbkrv80FQ1xjx125h6UyF96vN7CopJSGOfzFug 13 | UWn5PQcHxIMCsLFtdC9e/i/3CCnYUzVH/B+BiWr1CBVvm3KIaT02cc5eX8ZfwPoT 14 | nUrzrFP3c7QJGZLUfNbjO1CFeX3Mw9/3wXnBTZUYtfhVQu5KjXwlgAKhxGiF77Uo 15 | PgGzgfvH6c/aICuuqFEnV7HAy5tzdKucCtU94S8VT5SzVD7w4mtfnOyZS3IAj3vH 16 | 98TD1mmq/5C21C19REyIvWS+caRwLIKWelWTzc8YVjIg6v4PGUMqd6H2RmQDtCOT 17 | PND+XE31XGveLqErEr9ij6W+RrfR7r1i/cbQ5rUxNBS/8uX49+UzHh3DqY0CtunA 18 | S+Be2zvl15Mtf7Um/+caQlRYSo1W4fPiClYdHbIZ9OwoSqB6Kg5usX6oPnmOYqA/ 19 | AUswyfQlS1EFCPjBCwFRMtMW6QFUCEvnJKCOc5JpE/Rydqg/9ZUtX3djSNLw0v0s 20 | swIDAQABo0gwRjAiBgNVHREEGzAZghdkZW1vLmhuc2VjZmFrZWRvbWFpbi5pdDAL 21 | BgNVHQ8EBAMCBPAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQAD 22 | ggIBAFpTvSbQhKW/nJMu8dzFUO9z0g07o7BbwUu0idn2FpLUHQTIWqne2F/1Gt6L 23 | jPYiZo1tdDi1jG+0KkWugGRhYFgrYcIHqyVcGEWdQmpdzuUSakRTpehMwPFV3WHQ 24 | nKfcAUUSti1fpgcma7nTUI13C68Ne74YonqUD21EE85h1ROAbQ0cmIfyqBLXHw6q 25 | 0uKn49TPTKoIIlhPb6oGu2Ypvdtjre+VrcfV7iVsiFA48us1b2SZ57VxgO6+7NiW 26 | 2dLWmueEfpRgh4LXnE3y3xBDtFNTG+2qbTz/7lX7HgFgde4oxgAx7/GO9PKfn7Bc 27 | 0y7gDsKZK2hS2g+4QTdh7ObOi6bgd/vrql906D9LgoUhA72L7M9BDct4HBe2BnJ3 28 | oLi8ojyn2+SBqIJGRZQ1U2kka3drH2TOE4HsdJen2FL/GreZqVZBs6u5vVE1C4jE 29 | K/UiqERVNmrV0fGide6sEZwfsdgiIhhLukTHf5HDkCl5rSSu+mW5uXCIwcWiuj4P 30 | EdcQbSOW6TsDS1RWBYlsZG6b9RCCwhEbzuROS3N1/H5mqhz5tMgQAoUvi2eeyfBD 31 | +JqMwbnw2D4WBPpqFAKA4bXkG+qUZcKHrOKa8eG3QtZ1F6IyG4MjNIlVJX5RSmXn 32 | Y6ax27uieR/DmX50bdXpi6Mf2lVqqqhXsWHawQ5nyQEIfBgB 33 | -----END CERTIFICATE----- 34 | -------------------------------------------------------------------------------- /Demo/Android/certs/server-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQClXtRH6XX2AswI 3 | AoRkuNc1FHRnRYeZqkEs+fkEkVhOc0lqLj5WhXKXH5A594iJnf/tyX/73Y3gYtev 4 | K8+AD9slbq2w5uX8g1qxSRpJ55ima775lztV4AmNC96Kk9D6V6yEN3E8Xdb8aAgS 5 | XMM1ervJXW/rAZ5FrtzjmsEn90mmPAMI0DbEIErZhDuJ1aiGUig0etSbkrv80FQ1 6 | xjx125h6UyF96vN7CopJSGOfzFugUWn5PQcHxIMCsLFtdC9e/i/3CCnYUzVH/B+B 7 | iWr1CBVvm3KIaT02cc5eX8ZfwPoTnUrzrFP3c7QJGZLUfNbjO1CFeX3Mw9/3wXnB 8 | TZUYtfhVQu5KjXwlgAKhxGiF77UoPgGzgfvH6c/aICuuqFEnV7HAy5tzdKucCtU9 9 | 4S8VT5SzVD7w4mtfnOyZS3IAj3vH98TD1mmq/5C21C19REyIvWS+caRwLIKWelWT 10 | zc8YVjIg6v4PGUMqd6H2RmQDtCOTPND+XE31XGveLqErEr9ij6W+RrfR7r1i/cbQ 11 | 5rUxNBS/8uX49+UzHh3DqY0CtunAS+Be2zvl15Mtf7Um/+caQlRYSo1W4fPiClYd 12 | HbIZ9OwoSqB6Kg5usX6oPnmOYqA/AUswyfQlS1EFCPjBCwFRMtMW6QFUCEvnJKCO 13 | c5JpE/Rydqg/9ZUtX3djSNLw0v0sswIDAQABAoICAB4vEtwVVrySgh8sJnIj8tPz 14 | adO3sypvEvpcngEhblbTYCELU8GDFglTb4XFEpzbw+zpDH0yUUUVhXypqHQdcMiC 15 | TR2u52eRqioVx8CZjxlBLVhtLB1eiR+/fN0aYdhvzENNw1NbZXQooOCJIfBO+Jum 16 | fFkOsI7TfenlQ0BCOKUo6vze1EA0DLVrF1rvLpVlxYTerpO+0qUweU5fYoW4kToy 17 | ljhffBWHNPpvRg/Swx6d/ZPKedAHJEqHabsqq8LQ7nxOdyTO+ziqYi3IoUccNJ1s 18 | aBlR7CRV3W8IWDL+DCcsXJPhbmoKKmBG1LAIIUjUeLwEEtwfHHAiVlDcEkyyWiMa 19 | rJf0SZ0DC54f6a5AZRU9aubqoy8E5FdAh9xeJhHg1j4ulCOzH+U69eCKsl7PoHTm 20 | M2XK4m9GMUNFPE77kgzM98p2+ZPLGmk6Wz19tGeqm0ILYL4FLgUFHzUYvKbWOqXl 21 | ebF1NLg3nhZMkUmClNc5Alp7+7bfpRkBqYYII1VvqIYOm6QwgeSiHSD6emkPEzZR 22 | 1VMFrAH2X+bJIit7lBQWrP+sHMsrKt/89W4b6isxH3IIhRU16spLiq/bol8qML4X 23 | qqZb8gIxjmDh6APWOizz+CAK4Dyjwf5HlgSAHFH4nQLkOHhv+l2j1sWhm4IV/P2u 24 | 8eR7lVNUNePYu89Jh9IxAoIBAQDVkinbQ4IOZ80SfoBCPCJR6hl3idRdcCNFPwoE 25 | rBvgE/MCrMd8C4XnPQuDs3X+pLkId5C0+pkiESyw5T5EPTWY+NivnKgldtt3cMgZ 26 | JJKuv5dCeiVXTm60f+YyOVWjrlkwr8uiHzcUjFcOZ+rkkiyTD4JYh7lsTGsR9CK6 27 | PJNxZoBDhjjhZ4gxgBpb2MjB8j/f4gAHoPn2p92gNAJEetj6uRyrTXNkEZzqX2ZG 28 | lOdHzjLmvPcNxL+OB5FReXWPW3sskJiPzptfi0LmiH51ncU66/4przbEbVudzTpK 29 | aP4PPmOxEsTfSVLl7+AcnDYiXxQK8MRkS5zwopoUzSr8jTEnAoIBAQDGOUX1Qj27 30 | S/bRgZ/Dkqtihdph40yIUkV0FvSM/jOCvg4OZLbb0/96dZSVfdDWn9c6GFuMf9aa 31 | WRpeuKtawm/6dJ08M3aTFnfqktmzkac8mExolsQX2I3qz5h286KItUHBE8aXlKlK 32 | CeVbhPoJ8VMw+h9iMRfIqOqZec4ObxYbFJ4JUic84wjlUhM3tZim8mdP7kDeI52l 33 | QlcDHvLiU9wWMQKPMv0hlO3wQmIlIkef2vSNpo/1vbROOwqHOByHzxNn7Dn58nqZ 34 | t8NUwUzp7XozJumgejAkjyBq8xx4wV7N07csN7ymO2238KOLoHkFjI8ArW+4+BeR 35 | xkZX8OKHfIeVAoIBACq9LEknslAlr6FC1XHrzMZtvZjxaSElvrkty1hyBMYL1/ay 36 | Et2dBLfovHQnprZFyo2KN9gvyhNKMpJX3eUUBrTNH7fM9s1AgtzdIf3+FqiHyMJR 37 | 5UUaegb/DVcBqESwMEMQ+wQ/kOs4xncojpYdNnRbhy8mQhINHOQj4gs48hygcYyR 38 | wLJC4LtfMmcs88f0LZolBPKB4WPd5fkYT1fa6a8K+lTBlBTzfV5dpv7672msNv17 39 | 1UjZFahblVYjo0YD/ZtGeHFW3Dr7iHDP4daxsvV+ooc+/AvFAq+KkV6wsh7k0lhu 40 | NB2m88L2BPhUvnSUaFNkKTkU9JwnPgRppm+R5cMCggEAGom0af9yUE79PlNCVmae 41 | d1RARnat2lpZXg9ssiueLdTQAPFO4eNiAr0XJWRwvysrH2X/a7H5wzRGqYPDvSTm 42 | 5PsTyemB6HNLnqYGa1oGr8AcCqyUmDSv203rtZEso2zQ3s6M+eI3P+hPQo9tAkPC 43 | xX3XEchMzNqAfgempJwOs5VxCwJqqmYi3MC+PekcBM3TMwpMuPC/vE5DY/GSxuY/ 44 | QuFQ3GcRKJvNtyrMBPiv6iDgOM6h216IcyjIYg4LE8uPZ4xivqnN7CKEDibK606V 45 | 259MC+YP5/UZe/gMScZpsNi1X0AfC4pNSCD5ZEoZqbqFsquFl5fvX97RsBhZ25W6 46 | eQKCAQEAvj7gb78H4geWS2x+AQ/cjyLUydwsJbzbq8r7sTBHOO/YuEwubvg0uW5g 47 | Sfu5d+3z123hVn+jjud5cLLHn/M+76bxjeeowVI2ag9cvDImiLmPwaxD/Dmo4jFn 48 | GQKb7ukHuoSYGrIerogQmEAzNAMIPGA/PLRyL6RIeIKRpAGluKEnHFJ4RFiAm9kc 49 | JwwKYJ+HN7qb2pq5j9SdvcHaab1hi6n4SwIXrgxLPx2Z/fp+biSzuT/wqMDnn/RX 50 | V9lwMAFI7mL3Q1YdAxrHEMl85qkk1xZZA/azDPw5jERopysJIcNyonSU4c1w7L1s 51 | lWix5sLOVb8CqlKwfLU9jGNkJ0VO7w== 52 | -----END PRIVATE KEY----- 53 | -------------------------------------------------------------------------------- /Demo/Android/exportedPluginsDemoAndroid.csv: -------------------------------------------------------------------------------- 1 | 2;RGVjcnlwdF9jb250ZXh0;bXlkZWNyeXB0;3;RGVjcnlwdA==;4;;[];2;;[];[] 2 | 2;RW5jcnlwdF9jb250ZXh0;bXllbmNyeXB0;3;RW5jcnlwdA==;4;;[];2;;[];[] 3 | 1;4;;[];[];bXllbmNyeXB0;[];RGVjcnlwdF9tZXNzYWdlRWRpdG9yVGFi;bXlkZWNyeXB0;0;RW5jcnlwdA==;0;;3;;[];4;QnJpZGFEZWNyeXB0ZWQ=;[];[] 4 | 0;16,32;false;RW5jcnlwdFJlcXVlc3RzX0lIdHRwTGlzdGVuZXI=;bXllbmNyeXB0;1;RW5jcnlwdA==;0;;3;;[];6;;[];[] 5 | 0;16,32;false;RGVjcnlwdFJlc3BvbnNlc19JSHR0cExpc3RlbmVy;bXlkZWNyeXB0;2;RW5jcnlwdA==;0;;3;;[];6;;[];[] 6 | 3;0;false;RGVjcnlwdF9idXR0b24=;bXlkZWNyeXB0;4;RGVjcnlwdA==;7;;[];0;;[];[] 7 | 3;0;false;RW5jcnlwdF9idXR0b24=;bXllbmNyeXB0;4;RW5jcnlwdA==;7;;[];0;;[];[] 8 | -------------------------------------------------------------------------------- /Demo/Android/server.py: -------------------------------------------------------------------------------- 1 | import flask 2 | import base64 3 | from flask import request 4 | from Crypto.Cipher import AES 5 | from Crypto import Random 6 | from Crypto.Util.Padding import pad 7 | import random 8 | 9 | key = b"1234567890123456" 10 | plain = b"Here's your secret!" 11 | plain2 = b"Here's your data!" 12 | 13 | iv = b"jvHJ1XFt0IXBrxxx" 14 | 15 | app = flask.Flask(__name__) 16 | 17 | @app.route('/', methods=['GET', 'POST']) 18 | def handle_request(): 19 | cipher = AES.new(key, AES.MODE_CBC, iv) 20 | print(request.data) 21 | fulldata = cipher.decrypt(base64.urlsafe_b64decode(request.data)) 22 | print(fulldata) 23 | if b"getSecret" not in fulldata: 24 | cipher = AES.new(key, AES.MODE_CBC, iv) 25 | encoded = base64.urlsafe_b64encode(cipher.encrypt(pad(plain2, AES.block_size))) 26 | else: 27 | ciphere = AES.new(key, AES.MODE_CBC, iv) 28 | encoded = base64.urlsafe_b64encode(ciphere.encrypt(pad(plain, AES.block_size))) 29 | print(encoded) 30 | return encoded 31 | 32 | app.run(host="0.0.0.0", port=5000, debug=True) 33 | -------------------------------------------------------------------------------- /Demo/iOS/BridaTestAppBackend-0.0.1-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/Demo/iOS/BridaTestAppBackend-0.0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /Demo/iOS/DemoHIP.ipa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/Demo/iOS/DemoHIP.ipa -------------------------------------------------------------------------------- /Demo/iOS/DemoHITB.ipa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/Demo/iOS/DemoHITB.ipa -------------------------------------------------------------------------------- /Demo/iOS/DemoIOSBridaJS/brida.js: -------------------------------------------------------------------------------- 1 | const androidpinningwithca1 = require("./androidDefaultHooks.js").androidpinningwithca1 2 | const androidpinningwithoutca1 = require("./androidDefaultHooks.js").androidpinningwithoutca1 3 | const androidrooting1 = require("./androidDefaultHooks.js").androidrooting1 4 | const androidfingerprintbypass1 = require("./androidDefaultHooks.js").androidfingerprintbypass1 5 | const androidfingerprintbypass2hook = require("./androidDefaultHooks.js").androidfingerprintbypass2hook 6 | const androidfingerprintbypass2function = require("./androidDefaultHooks.js").androidfingerprintbypass2function 7 | const tracekeystore = require("./androidDefaultHooks.js").tracekeystore 8 | const listaliasesstatic = require("./androidDefaultHooks.js").listaliasesstatic 9 | const listaliasesruntime = require("./androidDefaultHooks.js").listaliasesruntime 10 | const dumpcryptostuff = require("./androidDefaultHooks.js").dumpcryptostuff 11 | const okhttphostnameverifier = require("./androidDefaultHooks.js").okhttphostnameverifier 12 | const ios10pinning = require("./iosDefaultHooks.js").ios10pinning 13 | const ios11pinning = require("./iosDefaultHooks.js").ios11pinning 14 | const ios12pinning = require("./iosDefaultHooks.js").ios12pinning 15 | const ios13pinning = require("./iosDefaultHooks.js").ios13pinning 16 | const iosbypasstouchid = require("./iosDefaultHooks.js").iosbypasstouchid 17 | const iosjailbreak = require("./iosDefaultHooks.js").iosjailbreak 18 | const iosdumpkeychain = require("./iosDefaultHooks.js").iosdumpkeychain 19 | const iosdataprotectionkeys = require("./iosDefaultHooks.js").iosdataprotectionkeys 20 | const iosdumpcurrentencryptedapp = require("./iosDefaultHooks.js").iosdumpcurrentencryptedapp 21 | const dumpcryptostuffios = require("./iosDefaultHooks.js").dumpcryptostuffios 22 | const demangle = require("./iosDefaultHooks.js").demangle 23 | const getallclasses = require("./bridaFunctions").getallclasses 24 | const getallmodules = require("./bridaFunctions").getallmodules 25 | const getmoduleimports = require("./bridaFunctions").getmoduleimports 26 | const getmoduleexports = require("./bridaFunctions").getmoduleexports 27 | const getclassmethods = require("./bridaFunctions").getclassmethods 28 | const findobjcmethods = require("./bridaFunctions").findobjcmethods 29 | const findjavamethods = require("./bridaFunctions").findjavamethods 30 | const findimports = require("./bridaFunctions").findimports 31 | const findexports = require("./bridaFunctions").findexports 32 | const detachall = require("./bridaFunctions").detachall 33 | const trace = require("./bridaFunctions").trace 34 | const changereturnvalue = require("./bridaFunctions").changereturnvalue 35 | const getplatform = require("./bridaFunctions").getplatform 36 | 37 | // Brida User file: use this file to insert your Frida exports/hooks/functions. 38 | // Do not remove existing code (it is necessary for Brida) 39 | 40 | rpc.exports = { 41 | androidpinningwithca1, androidpinningwithoutca1, androidrooting1, 42 | androidfingerprintbypass1, androidfingerprintbypass2hook, 43 | androidfingerprintbypass2function, tracekeystore, listaliasesstatic, 44 | listaliasesruntime, dumpcryptostuff, okhttphostnameverifier, 45 | ios10pinning, ios11pinning, ios12pinning, ios13pinning, 46 | iosbypasstouchid, iosjailbreak, iosdumpkeychain, iosdataprotectionkeys, 47 | iosdumpcurrentencryptedapp, dumpcryptostuffios, demangle, 48 | getallclasses, getallmodules, getmoduleimports, getmoduleexports, 49 | getclassmethods, findobjcmethods, findjavamethods, findimports, 50 | findexports, detachall, trace, changereturnvalue, getplatform, 51 | 52 | encrypt: function(content) { 53 | 54 | if (ObjC.available) { 55 | 56 | var encryptionClass = ObjC.classes.Encryption; 57 | return encodeURIComponent(encryptionClass.encryptRequest_(content).toString()); 58 | 59 | } 60 | 61 | }, 62 | 63 | decrypt: function(content) { 64 | 65 | if (ObjC.available) { 66 | 67 | var encryptionClass = ObjC.classes.Encryption; 68 | return encryptionClass.decryptResponse_(decodeURIComponent(content)).toString(); 69 | 70 | } 71 | 72 | }, 73 | 74 | encryptpassword: function(password) { 75 | 76 | if (ObjC.available) { 77 | 78 | var loginUtilClass = ObjC.classes.LoginUtil; 79 | return encodeURIComponent(loginUtilClass.encryptPassword_(password).toString()); 80 | 81 | } 82 | 83 | } 84 | 85 | } 86 | 87 | // Put here you Frida hooks! 88 | 89 | //if(ObjC.available) { 90 | //if(Java.available) { 91 | 92 | // ... 93 | 94 | //} 95 | 96 | 97 | 98 | // Auxiliary functions - You can remove them if you don't need them! 99 | 100 | // Convert a hex string to a byte array 101 | function hexToBytes(hex) { 102 | for (var bytes = [], c = 0; c < hex.length; c += 2) 103 | bytes.push(parseInt(hex.substr(c, 2), 16)); 104 | return bytes; 105 | } 106 | 107 | // Convert a ASCII string to a hex string 108 | function stringToHex(str) { 109 | return str.split("").map(function(c) { 110 | return ("0" + c.charCodeAt(0).toString(16)).slice(-2); 111 | }).join(""); 112 | } 113 | 114 | // Convert a hex string to a ASCII string 115 | function hexToString(hexStr) { 116 | var hex = hexStr.toString();//force conversion 117 | var str = ''; 118 | for (var i = 0; i < hex.length; i += 2) 119 | str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); 120 | return str; 121 | } 122 | 123 | // Convert a byte array to a hex string 124 | function bytesToHex(bytes) { 125 | for (var hex = [], i = 0; i < bytes.length; i++) { 126 | hex.push((bytes[i] >>> 4).toString(16)); 127 | hex.push((bytes[i] & 0xF).toString(16)); 128 | } 129 | return hex.join(""); 130 | } 131 | 132 | // Native ArrayBuffer to Base64 133 | // https://gist.github.com/jonleighton/958841 134 | function base64ArrayBuffer(arrayBuffer) { 135 | var base64 = '' 136 | var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 137 | 138 | var bytes = new Uint8Array(arrayBuffer) 139 | var byteLength = bytes.byteLength 140 | var byteRemainder = byteLength % 3 141 | var mainLength = byteLength - byteRemainder 142 | 143 | var a, b, c, d 144 | var chunk 145 | 146 | // Main loop deals with bytes in chunks of 3 147 | for (var i = 0; i < mainLength; i = i + 3) { 148 | // Combine the three bytes into a single integer 149 | chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2] 150 | 151 | // Use bitmasks to extract 6-bit segments from the triplet 152 | a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18 153 | b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12 154 | c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6 155 | d = chunk & 63 // 63 = 2^6 - 1 156 | 157 | // Convert the raw binary segments to the appropriate ASCII encoding 158 | base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d] 159 | } 160 | 161 | // Deal with the remaining bytes and padding 162 | if (byteRemainder == 1) { 163 | chunk = bytes[mainLength] 164 | 165 | a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2 166 | 167 | // Set the 4 least significant bits to zero 168 | b = (chunk & 3) << 4 // 3 = 2^2 - 1 169 | 170 | base64 += encodings[a] + encodings[b] + '==' 171 | } else if (byteRemainder == 2) { 172 | chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1] 173 | 174 | a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10 175 | b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4 176 | 177 | // Set the 2 least significant bits to zero 178 | c = (chunk & 15) << 2 // 15 = 2^4 - 1 179 | 180 | base64 += encodings[a] + encodings[b] + encodings[c] + '=' 181 | } 182 | 183 | return base64 184 | } 185 | -------------------------------------------------------------------------------- /Demo/iOS/DemoIOSBridaJS/bridaFunctions.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getallclasses, getallmodules, getmoduleimports, getmoduleexports, 3 | getclassmethods, findobjcmethods, findjavamethods, findimports, 4 | findexports, detachall, trace, changereturnvalue, getplatform 5 | } 6 | 7 | function getallclasses() { 8 | var result = [] 9 | if (ObjC.available) { 10 | for (var className in ObjC.classes) { 11 | if (ObjC.classes.hasOwnProperty(className)) { 12 | result.push(className); 13 | } 14 | } 15 | } else if(Java.available) { 16 | Java.perform(function() { 17 | Java.enumerateLoadedClasses({ 18 | onMatch: function (className) { 19 | result.push(className); 20 | }, 21 | onComplete: function() { 22 | } 23 | }); 24 | }); 25 | } 26 | return result; 27 | } 28 | 29 | function getallmodules() { 30 | var results = {} 31 | var matches = Process.enumerateModules( { 32 | onMatch: function (module) { 33 | results[module['name']] = module['base']; 34 | }, 35 | onComplete: function () { 36 | } 37 | }); 38 | return results; 39 | } 40 | 41 | function getmoduleimports(importname) { 42 | var results = {} 43 | var matches = Module.enumerateImports(importname, { 44 | onMatch: function (module) { 45 | results[module['type'] + ": " + module['name']] = module['address']; 46 | }, 47 | onComplete: function () { 48 | } 49 | }); 50 | return results; 51 | } 52 | 53 | function getmoduleexports(exportname) { 54 | var results = {} 55 | var matches = Module.enumerateExports(exportname, { 56 | onMatch: function (module) { 57 | results[module['type'] + ": " + module['name']] = module['address']; 58 | }, 59 | onComplete: function () { 60 | } 61 | }); 62 | return results; 63 | } 64 | 65 | function getclassmethods(classname) { 66 | var results = {} 67 | if (ObjC.available) { 68 | var resolver = new ApiResolver("objc"); 69 | var matches = resolver.enumerateMatches("*[" + classname + " *]", { 70 | onMatch: function (match) { 71 | results[match['name']] = match['address']; 72 | }, 73 | onComplete: function () { 74 | } 75 | }); 76 | } else if(Java.available) { 77 | Java.perform(function() { 78 | results = getJavaMethodArgumentTypes(classname); 79 | }); 80 | } 81 | return results; 82 | } 83 | 84 | function findjavamethods(searchstring) { 85 | var results = {} 86 | if(Java.available) { 87 | Java.perform(function() { 88 | var groups = [] 89 | groups.push(Java.enumerateMethods('*' + searchstring + '*!*/s')) 90 | groups.push(Java.enumerateMethods('*!*' + searchstring + '*/s')) 91 | groups.forEach(g => { 92 | g.forEach(classLoader => { 93 | classLoader.classes.forEach(c => { 94 | var className = c.name; 95 | c.methods.forEach(m => { 96 | var methodSignature = className + "!" + m; 97 | results[methodSignature] = null; 98 | }); 99 | }); 100 | }); 101 | }); 102 | }); 103 | } 104 | return results; 105 | } 106 | 107 | 108 | 109 | function findobjcmethods(searchstring) { 110 | var results = {} 111 | var resolver = new ApiResolver("objc"); 112 | var matches = resolver.enumerateMatches("*[*" + searchstring + "* *]", { 113 | onMatch: function (match) { 114 | results[match['name']] = match['address']; 115 | }, 116 | onComplete: function () { 117 | } 118 | }); 119 | matches = resolver.enumerateMatches("*[* *" + searchstring + "*]", { 120 | onMatch: function (match) { 121 | results[match['name']] = match['address']; 122 | }, 123 | onComplete: function () { 124 | } 125 | }); 126 | return results; 127 | } 128 | 129 | function findimports(searchstring) { 130 | var results = {} 131 | var resolver = new ApiResolver("module"); 132 | var matches = resolver.enumerateMatches("imports:*" + searchstring + "*!*", { 133 | onMatch: function (match) { 134 | results[match['name']] = match['address']; 135 | }, 136 | onComplete: function () { 137 | } 138 | }); 139 | matches = resolver.enumerateMatches("imports:*!*" + searchstring + "*", { 140 | onMatch: function (match) { 141 | results[match['name']] = match['address']; 142 | }, 143 | onComplete: function () { 144 | } 145 | }); 146 | return results; 147 | } 148 | 149 | function findexports(searchstring) { 150 | var results = {} 151 | var resolver = new ApiResolver("module"); 152 | var matches = resolver.enumerateMatches("exports:*" + searchstring + "*!*", { 153 | onMatch: function (match) { 154 | results[match['name']] = match['address']; 155 | }, 156 | onComplete: function () { 157 | } 158 | }); 159 | matches = resolver.enumerateMatches("exports:*!*" + searchstring + "*", { 160 | onMatch: function (match) { 161 | results[match['name']] = match['address']; 162 | }, 163 | onComplete: function () { 164 | } 165 | }); 166 | return results; 167 | } 168 | 169 | function detachall() { 170 | Interceptor.detachAll(); 171 | } 172 | 173 | // generic trace 174 | function trace(pattern,type,backtrace) { 175 | // SINGLE EXPORT (ALL EXPORT OF A MODULE CAN BE A MESS AND CRASH THE APP) 176 | if(type == "export") { 177 | var res = new ApiResolver("module"); 178 | pattern = "exports:" + pattern; 179 | var matches = res.enumerateMatchesSync(pattern); 180 | var targets = uniqBy(matches, JSON.stringify); 181 | targets.forEach(function(target) { 182 | traceModule(target.address, target.name, backtrace); 183 | }); 184 | //OBJC 185 | } else if(type.startsWith("objc")) { 186 | if (ObjC.available) { 187 | var res; 188 | if(type === "objc_class") { 189 | res = new ApiResolver("objc"); 190 | pattern = "*[" + pattern + " *]"; 191 | } else if(type === "objc_method") { 192 | res = new ApiResolver("objc"); 193 | } 194 | var matches = res.enumerateMatchesSync(pattern); 195 | var targets = uniqBy(matches, JSON.stringify); 196 | targets.forEach(function(target) { 197 | traceObjC(target.address, target.name,backtrace); 198 | }); 199 | } 200 | // ANDROID 201 | } else if(type.startsWith("java")) { 202 | if(Java.available) { 203 | Java.perform(function() { 204 | if(type === "java_class") { 205 | var methodsDictionary = getJavaMethodArgumentTypes(pattern); 206 | var targets = Object.keys(methodsDictionary); 207 | targets.forEach(function(targetMethod) { 208 | traceJavaMethod(targetMethod,backtrace); 209 | }); 210 | } else { 211 | traceJavaMethod(pattern,backtrace); 212 | } 213 | }); 214 | } 215 | } 216 | } 217 | 218 | function changereturnvalue(pattern, type, typeret, newret) { 219 | if(ObjC.available) { 220 | changeReturnValueIOS(pattern, type, typeret, newret); 221 | } else if(Java.available) { 222 | Java.perform(function() { 223 | changeReturnValueAndroid(pattern, type, typeret, newret); 224 | }); 225 | } else { 226 | changeReturnValueGeneric(pattern, type, typeret, newret); 227 | } 228 | } 229 | 230 | function getplatform() { 231 | 232 | if(Java.available) { 233 | return 0; 234 | } else if(ObjC.available){ 235 | return 1; 236 | } else { 237 | return 2; 238 | } 239 | 240 | } 241 | 242 | 243 | /* 244 | This method is used to get Java methods with arguments in bytecode syntex. By simply calling the getDeclaredMethods of a Java Class object 245 | and then calling toString on each Method object we do not get types in bytecode format. For example we get 'byte[]' instead of 246 | '[B'. This function uses overload object of frida to get types in correct bytecode form. 247 | */ 248 | function getJavaMethodArgumentTypes(classname) { 249 | if(Java.available) { 250 | var results = {}; 251 | Java.perform(function() { 252 | var hook = Java.use(classname); 253 | var res = hook.class.getDeclaredMethods(); 254 | res.forEach(function(s) { 255 | //console.log("s " + s); 256 | var targetClassMethod = parseJavaMethod(s.toString()); 257 | //console.log("targetClassMethod " + targetClassMethod); 258 | var delim = targetClassMethod.lastIndexOf("."); 259 | if (delim === -1) return; 260 | var targetClass = targetClassMethod.slice(0, delim) 261 | var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length) 262 | //console.log("targetClass " + targetClass); 263 | //console.log("targetMethod " + targetMethod); 264 | var hookClass = Java.use(targetClass); 265 | var classMethodOverloads = hookClass[targetMethod].overloads; 266 | classMethodOverloads.forEach(function(cmo) { 267 | // overload.argumentTypes is an array of objects representing the arguments. In the "className" field of each object there 268 | // is the bytecode form of the class of the current argument 269 | var argumentTypes = cmo.argumentTypes; 270 | var argumentTypesArray = [] 271 | argumentTypes.forEach(function(cmo) { 272 | argumentTypesArray.push(cmo.className); 273 | }); 274 | var argumentTypesString = argumentTypesArray.toString(); 275 | // overload.returnType.className contain the bytecode form of the class of the return value 276 | var currentReturnType = cmo.returnType.className; 277 | var newPattern = currentReturnType + " " + targetClassMethod + "(" + argumentTypesString + ")"; 278 | //console.log(newPattern); 279 | results[newPattern] = 0; 280 | }); 281 | hookClass.$dispose; 282 | }); 283 | hook.$dispose; 284 | }); 285 | return results; 286 | } 287 | } 288 | 289 | function changeReturnValueIOS(pattern, type, typeret, newret) { 290 | var res; 291 | if(type === "objc_method") { 292 | res = new ApiResolver("objc"); 293 | } else { 294 | // SINGLE EXPORT 295 | res = new ApiResolver("module"); 296 | pattern = "exports:" + pattern; 297 | } 298 | var matches = res.enumerateMatchesSync(pattern); 299 | var targets = uniqBy(matches, JSON.stringify); 300 | targets.forEach(function(target) { 301 | Interceptor.attach(target.address, { 302 | onEnter: function(args) { 303 | }, 304 | onLeave: function(retval) { 305 | if(typeret === "String") { 306 | var a1 = ObjC.classes.NSString.stringWithString_(newret); 307 | try { 308 | console.log("*** " + pattern + " Replacing " + ObjC.Object(retval) + " with " + a1); 309 | } catch(err) { 310 | console.log("*** " + pattern + " Replacing " + retval + " with " + a1); 311 | } 312 | retval.replace(a1); 313 | } else if(typeret === "Ptr") { 314 | console.log("*** " + pattern + " Replacing " + ptr(retval) + " with " + ptr(newret)); 315 | retval.replace(ptr(newret)); 316 | } else if(typeret === "Boolean") { 317 | if(newret === "true") { 318 | var toRet = 1; 319 | } else { 320 | var toRet = 0; 321 | } 322 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 323 | retval.replace(toRet); 324 | } else { 325 | console.log("*** " + pattern + " Replacing " + retval + " with " + newret); 326 | retval.replace(newret); 327 | } 328 | } 329 | }); 330 | }); 331 | console.log("*** Replacing return value of " + pattern + " with " + newret); 332 | } 333 | 334 | function changeReturnValueGeneric(pattern, type, typeret, newret) { 335 | var res = new ApiResolver("module"); 336 | pattern = "exports:" + pattern; 337 | var matches = res.enumerateMatchesSync(pattern); 338 | var targets = uniqBy(matches, JSON.stringify); 339 | targets.forEach(function(target) { 340 | Interceptor.attach(target.address, { 341 | onEnter: function(args) { 342 | }, 343 | onLeave: function(retval) { 344 | if(typeret === "Ptr") { 345 | console.log("*** " + pattern + " Replacing " + ptr(retval) + " with " + ptr(newret)); 346 | retval.replace(ptr(newret)); 347 | } else if(typeret === "Boolean") { 348 | if(newret === "true") { 349 | var toRet = 1; 350 | } else { 351 | var toRet = 0; 352 | } 353 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 354 | retval.replace(toRet); 355 | } else { 356 | console.log("*** " + pattern + " Replacing " + retval + " with " + newret); 357 | retval.replace(newret); 358 | } 359 | } 360 | }); 361 | }); 362 | console.log("*** Replacing return value of " + pattern + " with " + newret); 363 | } 364 | 365 | function changeReturnValueAndroid(pattern, type, typeret, newret) { 366 | if(type === "java_method") { 367 | var targetClassMethod = parseJavaMethod(pattern); 368 | //console.log(targetClassMethod); 369 | var argsTargetClassMethod = getJavaMethodArguments(pattern); 370 | //console.log(argsTargetClassMethod); 371 | var delim = targetClassMethod.lastIndexOf("."); 372 | if (delim === -1) return; 373 | var targetClass = targetClassMethod.slice(0, delim) 374 | var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length) 375 | //console.log(targetClass); 376 | //console.log(targetMethod); 377 | var hook = Java.use(targetClass); 378 | hook[targetMethod].overload.apply(hook[targetMethod],argsTargetClassMethod).implementation = function() { 379 | var retval = this[targetMethod].apply(this, arguments); 380 | var toRet = newret; 381 | if(typeret === "String") { 382 | var stringClass = Java.use("java.lang.String"); 383 | toRet = stringClass.$new(newret); 384 | } else if(typeret === "Ptr") { 385 | toRet = ptr(newret); 386 | } else if(typeret === "Boolean") { 387 | if(newret === "true") { 388 | toRet = true; 389 | } else { 390 | toRet = false; 391 | } 392 | } 393 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 394 | return toRet; 395 | } 396 | // SINGLE EXPORT 397 | } else { 398 | var res = new ApiResolver("module"); 399 | var pattern = "exports:" + pattern; 400 | var matches = res.enumerateMatchesSync(pattern); 401 | var targets = uniqBy(matches, JSON.stringify); 402 | targets.forEach(function(target) { 403 | Interceptor.attach(target.address, { 404 | onEnter: function(args) { 405 | }, 406 | onLeave: function(retval) { 407 | var toRet = newret; 408 | if(typeret === "String") { 409 | var stringClass = Java.use("java.lang.String"); 410 | var toRet = stringClass.$new(newret); 411 | } else if(typeret === "ptr") { 412 | toRet = ptr(newret); 413 | } else if(typeret === "Boolean") { 414 | if(newret === "true") { 415 | var toRet = 1; 416 | } else { 417 | var toRet = 0; 418 | } 419 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 420 | retval.replace(toRet); 421 | } 422 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 423 | retval.replace(toRet); 424 | } 425 | }); 426 | }); 427 | } 428 | console.log("*** Replacing return value of " + pattern + " with " + newret); 429 | } 430 | 431 | // trace ObjC methods 432 | function traceObjC(impl, name, backtrace) { 433 | console.log("*** Tracing " + name); 434 | Interceptor.attach(impl, { 435 | onEnter: function(args) { 436 | console.log("*** entered " + name); 437 | console.log("Caller: " + DebugSymbol.fromAddress(this.returnAddress)); 438 | // print args 439 | if (name.indexOf(":") !== -1) { 440 | console.log("Parameters:"); 441 | var par = name.split(":"); 442 | par[0] = par[0].split(" ")[1]; 443 | for (var i = 0; i < par.length - 1; i++) { 444 | printArg(par[i] + ": ", args[i + 2]); 445 | } 446 | } 447 | if(backtrace === "true") { 448 | console.log("Backtrace:\n\t" + Thread.backtrace(this.context, Backtracer.ACCURATE) 449 | .map(DebugSymbol.fromAddress).join("\n\t")); 450 | } 451 | }, 452 | onLeave: function(retval) { 453 | console.log("*** exiting " + name); 454 | console.log("Return value:"); 455 | printArg("retval: ", retval); 456 | } 457 | }); 458 | } 459 | 460 | 461 | 462 | // trace a specific Java Method 463 | function traceJavaMethod(pattern,backtrace) { 464 | var targetClassMethod = parseJavaMethod(pattern); 465 | //console.log(targetClassMethod); 466 | var argsTargetClassMethod = getJavaMethodArguments(pattern); 467 | //console.log(argsTargetClassMethod); 468 | var delim = targetClassMethod.lastIndexOf("."); 469 | if (delim === -1) return; 470 | var targetClass = targetClassMethod.slice(0, delim) 471 | var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length) 472 | var hook = Java.use(targetClass); 473 | //var overloadCount = hook[targetMethod].overloads.length; 474 | console.log("*** Tracing " + pattern); 475 | hook[targetMethod].overload.apply(hook[targetMethod],argsTargetClassMethod).implementation = function() { 476 | console.log("*** entered " + targetClassMethod); 477 | // print args 478 | if (arguments.length) console.log("Parameters:"); 479 | for (var j = 0; j < arguments.length; j++) { 480 | console.log("\targ[" + j + "]: " + arguments[j]); 481 | } 482 | // print backtrace 483 | if(backtrace === "true") { 484 | Java.perform(function() { 485 | var threadClass = Java.use("java.lang.Thread"); 486 | var currentThread = threadClass.currentThread(); 487 | var currentStackTrace = currentThread.getStackTrace(); 488 | console.log("Backtrace:"); 489 | currentStackTrace.forEach(function(st) { 490 | console.log("\t" + st.toString()); 491 | }); 492 | }); 493 | } 494 | // print retval 495 | var retval = this[targetMethod].apply(this, arguments); 496 | console.log("*** exiting " + targetClassMethod); 497 | console.log("Return value:"); 498 | console.log("\tretval: " + retval); 499 | return retval; 500 | } 501 | } 502 | 503 | // trace Module functions 504 | function traceModule(impl, name, backtrace) { 505 | console.log("*** Tracing " + name); 506 | Interceptor.attach(impl, { 507 | onEnter: function(args) { 508 | console.log("*** entered " + name); 509 | if(backtrace === "true") { 510 | console.log("Backtrace:\n\t" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n\t")); 511 | } 512 | }, 513 | onLeave: function(retval) { 514 | console.log("*** exiting " + name); 515 | console.log("Return value:"); 516 | if(ObjC.available) { 517 | printArg("retval: ", retval); 518 | } else { 519 | console.log("\tretval: ", retval); 520 | } 521 | } 522 | }); 523 | } 524 | 525 | // print helper 526 | function printArg(desc, arg) { 527 | if(arg != 0x0) { 528 | try { 529 | var objectArg = ObjC.Object(arg); 530 | console.log("\t(" + objectArg.$className + ") " + desc + objectArg.toString()); 531 | } catch(err2) { 532 | console.log("\t" + desc + arg); 533 | } 534 | } else { 535 | console.log("\t" + desc + "0x0"); 536 | } 537 | } 538 | 539 | // remove duplicates from array 540 | function uniqBy(array, key) { 541 | var seen = {}; 542 | return array.filter(function(item) { 543 | var k = key(item); 544 | return seen.hasOwnProperty(k) ? false : (seen[k] = true); 545 | }); 546 | } 547 | 548 | /* 549 | INPUT LIKE: public boolean a.b.functionName(java.lang.String) 550 | OUTPUT LIKE: a.b.functionName 551 | */ 552 | function parseJavaMethod(method) { 553 | var parSplit = method.split("("); 554 | var spaceSplit = parSplit[0].split(" "); 555 | return spaceSplit[spaceSplit.length - 1]; 556 | } 557 | 558 | //INPUT LIKE: public boolean a.b.functionName(java.lang.String,java.lang.String) 559 | //OUTPUT LIKE: ["java.lang.String","java.lang.String"] 560 | function getJavaMethodArguments(method) { 561 | var m = method.match(/.*\((.*)\).*/); 562 | if(m[1] !== "") { 563 | return m[1].split(","); 564 | } else { 565 | return []; 566 | } 567 | } 568 | 569 | -------------------------------------------------------------------------------- /Demo/iOS/OldCompiledPlugins/BridaDemoLoginPlugin-0.0.1-SNAPSHOT-jar-with-dependencies.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/Demo/iOS/OldCompiledPlugins/BridaDemoLoginPlugin-0.0.1-SNAPSHOT-jar-with-dependencies.jar -------------------------------------------------------------------------------- /Demo/iOS/OldCompiledPlugins/BridaDemoSearchPlugin-0.0.1-SNAPSHOT-jar-with-dependencies.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/Demo/iOS/OldCompiledPlugins/BridaDemoSearchPlugin-0.0.1-SNAPSHOT-jar-with-dependencies.jar -------------------------------------------------------------------------------- /Demo/iOS/OldCompiledPlugins/Sources/BridaDemoLoginPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.fd 6 | BridaDemoLoginPlugin 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | BridaDemoLoginPlugin 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | maven-assembly-plugin 23 | 24 | 25 | 26 | true 27 | 28 | 29 | jar-with-dependencies 30 | 31 | 32 | 33 | 34 | true 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | make-assembly 43 | package 44 | 45 | single 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | net.portswigger.burp.extender 58 | burp-extender-api 59 | 1.7.22 60 | 61 | 62 | 63 | junit 64 | junit 65 | 3.8.1 66 | test 67 | 68 | 69 | 70 | 71 | net.razorvine 72 | pyrolite 73 | 4.20 74 | 75 | 76 | 77 | 78 | net.razorvine 79 | serpent 80 | 1.23 81 | 82 | 83 | 84 | 85 | org.apache.commons 86 | commons-lang3 87 | 3.7 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Demo/iOS/OldCompiledPlugins/Sources/BridaDemoLoginPlugin/src/main/java/burp/BurpExtender.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | import java.util.Arrays; 6 | import java.util.HashMap; 7 | import java.util.Set; 8 | 9 | import org.apache.commons.lang3.ArrayUtils; 10 | 11 | import net.razorvine.pyro.PyroProxy; 12 | import net.razorvine.pyro.PyroURI; 13 | 14 | public class BurpExtender implements IBurpExtender, IHttpListener { 15 | 16 | private PrintWriter stdout; 17 | private PrintWriter stderr; 18 | 19 | private IBurpExtenderCallbacks callbacks; 20 | private IExtensionHelpers helpers; 21 | 22 | public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { 23 | 24 | // Set the name of the extension 25 | callbacks.setExtensionName("Brida Demo Login Plugin"); 26 | 27 | // Initialize stdout and stderr (configurable from the Extension pane) 28 | stdout = new PrintWriter(callbacks.getStdout(), true); 29 | stderr = new PrintWriter(callbacks.getStderr(), true); 30 | 31 | // Save references to useful objects 32 | this.callbacks = callbacks; 33 | this.helpers = callbacks.getHelpers(); 34 | 35 | // Register ourselves as an HttpListener, in this way all requests and responses will be forwarded to us 36 | callbacks.registerHttpListener(this); 37 | 38 | } 39 | 40 | /* 41 | * int toolFlag: A flag indicating the Burp tool that issued the request. Burp tool flags are defined in the IBurpExtenderCallbacks interface. 42 | * boolean messageIsRequest: Flags whether the method is being invoked for a request or response. 43 | * IHttpRequestResponse messageInfo: Details of the request / response to be processed. Extensions can call the setter methods on this object to update the current message and so modify Burp's behavior. 44 | */ 45 | public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { 46 | 47 | // Process only Repeater, Scanner and Intruder requests 48 | if(toolFlag == IBurpExtenderCallbacks.TOOL_SCANNER || 49 | toolFlag == IBurpExtenderCallbacks.TOOL_REPEATER || 50 | toolFlag == IBurpExtenderCallbacks.TOOL_INTRUDER) { 51 | 52 | // Modify "test" parameter of Repeater requests 53 | if(messageIsRequest) { 54 | 55 | // Get request bytes 56 | byte[] request = messageInfo.getRequest(); 57 | 58 | // Get a IRequestInfo object, useful to work with the request 59 | IRequestInfo requestInfo = helpers.analyzeRequest(request); 60 | 61 | // Get "test" parameter 62 | IParameter passwordParameter = helpers.getRequestParameter(request, "password"); 63 | 64 | if(passwordParameter != null) { 65 | 66 | String urlDecodedPasswordParameterValue = helpers.urlDecode(passwordParameter.getValue()); 67 | 68 | String ret = ""; 69 | 70 | // Ask Brida to encrypt our attack vector 71 | String pyroUrl = "PYRO:BridaServicePyro@localhost:9999"; 72 | 73 | try { 74 | 75 | PyroProxy pp = new PyroProxy(new PyroURI(pyroUrl)); 76 | ret = (String)pp.call("callexportfunction","encryptpassword",new String[]{urlDecodedPasswordParameterValue}); 77 | pp.close(); 78 | 79 | } catch(Exception e) { 80 | 81 | stderr.println(e.toString()); 82 | StackTraceElement[] exceptionElements = e.getStackTrace(); 83 | for(int i=0; i< exceptionElements.length; i++) { 84 | stderr.println(exceptionElements[i].toString()); 85 | } 86 | 87 | } 88 | 89 | 90 | // Create the new parameter 91 | IParameter newTestParameter = helpers.buildParameter(passwordParameter.getName(), ret, passwordParameter.getType()); 92 | 93 | // Create the new request with the updated parameter 94 | byte[] newRequest = helpers.updateParameter(request, newTestParameter); 95 | 96 | // Update the messageInfo object with the modified request (otherwise the request remains the old one) 97 | messageInfo.setRequest(newRequest); 98 | 99 | 100 | } 101 | 102 | } 103 | 104 | } 105 | 106 | 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /Demo/iOS/OldCompiledPlugins/Sources/BridaDemoSearchPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.fd 6 | BridaDemoSearchPlugin 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | BridaDemoSearchPlugin 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | maven-assembly-plugin 23 | 24 | 25 | 26 | true 27 | 28 | 29 | jar-with-dependencies 30 | 31 | 32 | 33 | 34 | true 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | make-assembly 43 | package 44 | 45 | single 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | net.portswigger.burp.extender 58 | burp-extender-api 59 | 1.7.22 60 | 61 | 62 | 63 | junit 64 | junit 65 | 3.8.1 66 | test 67 | 68 | 69 | 70 | 71 | net.razorvine 72 | pyrolite 73 | 4.20 74 | 75 | 76 | 77 | 78 | net.razorvine 79 | serpent 80 | 1.23 81 | 82 | 83 | 84 | 85 | org.apache.commons 86 | commons-lang3 87 | 3.7 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Demo/iOS/OldCompiledPlugins/Sources/BridaDemoSearchPlugin/src/main/java/burp/BurpExtender.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.io.PrintWriter; 4 | import java.util.Arrays; 5 | 6 | import org.apache.commons.lang3.ArrayUtils; 7 | 8 | import net.razorvine.pyro.PyroProxy; 9 | import net.razorvine.pyro.PyroURI; 10 | 11 | public class BurpExtender implements IBurpExtender, IHttpListener { 12 | 13 | private PrintWriter stdout; 14 | private PrintWriter stderr; 15 | 16 | private IBurpExtenderCallbacks callbacks; 17 | private IExtensionHelpers helpers; 18 | 19 | public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { 20 | 21 | // Set the name of the extension 22 | callbacks.setExtensionName("Brida Demo Search Plugin"); 23 | 24 | // Initialize stdout and stderr (configurable from the Extension pane) 25 | stdout = new PrintWriter(callbacks.getStdout(), true); 26 | stderr = new PrintWriter(callbacks.getStderr(), true); 27 | 28 | // Save references to useful objects 29 | this.callbacks = callbacks; 30 | this.helpers = callbacks.getHelpers(); 31 | 32 | // Register ourselves as an HttpListener, in this way all requests and responses will be forwarded to us 33 | callbacks.registerHttpListener(this); 34 | 35 | } 36 | 37 | public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { 38 | 39 | // Process only Repeater, Scanner and Intruder requests 40 | if(toolFlag == IBurpExtenderCallbacks.TOOL_SCANNER || 41 | toolFlag == IBurpExtenderCallbacks.TOOL_REPEATER || 42 | toolFlag == IBurpExtenderCallbacks.TOOL_INTRUDER) { 43 | 44 | // Modify "test" parameter of Repeater requests 45 | if(messageIsRequest) { 46 | 47 | // Get request bytes 48 | byte[] request = messageInfo.getRequest(); 49 | 50 | // Get a IRequestInfo object, useful to work with the request 51 | IRequestInfo requestInfo = helpers.analyzeRequest(request); 52 | 53 | // Get "test" parameter 54 | IParameter contentParameter = helpers.getRequestParameter(request, "content"); 55 | 56 | if(contentParameter != null) { 57 | 58 | String urlDecodedContentParameterValue = helpers.urlDecode(contentParameter.getValue()); 59 | 60 | String ret = ""; 61 | 62 | // Ask Brida to encrypt our attack vector 63 | String pyroUrl = "PYRO:BridaServicePyro@localhost:9999"; 64 | 65 | try { 66 | 67 | PyroProxy pp = new PyroProxy(new PyroURI(pyroUrl)); 68 | ret = (String)pp.call("callexportfunction","encrypt",new String[]{urlDecodedContentParameterValue}); 69 | pp.close(); 70 | 71 | } catch(Exception e) { 72 | 73 | stderr.println(e.toString()); 74 | StackTraceElement[] exceptionElements = e.getStackTrace(); 75 | for(int i=0; i< exceptionElements.length; i++) { 76 | stderr.println(exceptionElements[i].toString()); 77 | } 78 | } 79 | 80 | 81 | // Create the new parameter 82 | IParameter newTestParameter = helpers.buildParameter(contentParameter.getName(), ret, contentParameter.getType()); 83 | 84 | // Create the new request with the updated parameter 85 | byte[] newRequest = helpers.updateParameter(request, newTestParameter); 86 | 87 | // Update the messageInfo object with the modified request (otherwise the request remains the old one) 88 | messageInfo.setRequest(newRequest); 89 | 90 | 91 | } 92 | 93 | // Response 94 | } else { 95 | 96 | // Get request bytes in order to check if the request contain "content" parameter 97 | byte[] request = messageInfo.getRequest(); 98 | IRequestInfo requestInfo = helpers.analyzeRequest(request); 99 | IParameter contentParameter = helpers.getRequestParameter(request, "content"); 100 | 101 | if(contentParameter != null) { 102 | 103 | // Get response bytes 104 | byte[] response = messageInfo.getResponse(); 105 | 106 | // Get a IResponseInfo object, useful to work with the request 107 | IResponseInfo responseInfo = helpers.analyzeResponse(response); 108 | 109 | // Get the offset of the body 110 | int bodyOffset = responseInfo.getBodyOffset(); 111 | 112 | // Get the body (byte array and String) 113 | byte[] body = Arrays.copyOfRange(response, bodyOffset, response.length); 114 | String bodyString = helpers.bytesToString(body); 115 | 116 | String ret = ""; 117 | 118 | // Ask Brida to decrypt the response 119 | String pyroUrl = "PYRO:BridaServicePyro@localhost:9999"; 120 | 121 | try { 122 | 123 | PyroProxy pp = new PyroProxy(new PyroURI(pyroUrl)); 124 | ret = (String)pp.call("callexportfunction","decrypt",new String[]{bodyString}); 125 | pp.close(); 126 | 127 | } catch(Exception e) { 128 | 129 | stderr.println(e.toString()); 130 | StackTraceElement[] exceptionElements = e.getStackTrace(); 131 | for(int i=0; i< exceptionElements.length; i++) { 132 | stderr.println(exceptionElements[i].toString()); 133 | } 134 | } 135 | 136 | // Update the messageInfo object with the modified request (otherwise the request remains the old one) 137 | byte[] newResponse = ArrayUtils.addAll(Arrays.copyOfRange(response, 0, bodyOffset),ret.getBytes()); 138 | messageInfo.setResponse(newResponse); 139 | 140 | } 141 | 142 | } 143 | 144 | } 145 | 146 | 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /Demo/iOS/README.md: -------------------------------------------------------------------------------- 1 | # iOS Demo 2 | 3 | In this folder there is the iOS demo application used to show Brida features during **Hack In The Box 2018 Amsterdam** and **Hack In Paris 2021** conferences. 4 | 5 | I published both the versions but it is the same applications with different logos. 6 | 7 | The backend is a Spring Boot application that communicates with a MySQL database on localhost (default port 3306). The application logs in in the database using username root with empty password (I know, very very bad), so maybe run it on a firewalled system/VM ;) 8 | 9 | The code of the demo applications and of the backend have been developed very fast and can include tons of errors/vulnerabilities/etc. Consider it before letting them run in a publicly exposed server! Forgive us, it is a demo! ;) 10 | 11 | ## Run the demo 12 | 13 | 1. Install one of the two demo applications on the device. If the device is not jailbroken the application must be resigned in order to work. 14 | 2. Start MySQL/MariaDB database and import the supplied "dbBackendBridaDemo.db" file 15 | 16 | ```mysql -u root -p < dbBackendBridaDemo.db``` 17 | 18 | 3. Run the Spring Boot server application (it may not work with last versions of Java. It works well with Java 8) 19 | 20 | ```java -jar BridaTestAppBackend-0.0.1-SNAPSHOT.jar --server.port=8081``` 21 | 22 | 4. Run the demo application. 23 | 5. Set the IP address and port of the server application in the "Settings" tab and click on "Save settings" 24 | 6. Now the demo should work correctly 25 | 26 | ## Load Brida plugins 27 | 28 | 1. Configure Brida (refer to the [documentation](https://github.com/federicodotta/Brida/wiki/Start) for details). The application ID of the demo is **org.hitb.BridaDemo** 29 | 2. Click on the "Select folder" button of the "Frida JS files folder" and select the supplied "DemoIOSFridaJS" folder 30 | 3. Load the plugins, by clicking on the button "Import plugins" in the "Custom plugins" tab and choosing the supplied "exportedPluginsDemoIOS.csv" file 31 | 4. Enable the plugin(s) you want to try using the corresponding "Enable" button in the same tab 32 | 5. Spawn/attach the application 33 | 34 | ## Supplied Brida plugins 35 | 36 | The following Brida plugin can be used to handle the encryption in the **Search** tab of the demo iOS application. Encryption is also used in the **Login** tab of the demo and the Frida exported function to handle it is present in the supplied JS Brida files. Plugins for the Login functionality can be created using the same approach of the Search ones (by clicking on "Edit" on a plugin it is possible to see all the plugin configurations). 37 | 38 | 1. **Search_DecryptContext**: adds an entry to the context menu that decrypts the higlighted value using Brida, replacing it with its decrypted form, if possible. If the highlighted value is in a non-editable pane, a pop-up appears 39 | 2. **Search_EncryptContext**: adds an entry to the context menu that encrypts the higlighted value using Brida, replacing it with its encrypted form, if possible. If the highlighted value is in a non-editable pane, a pop-up appears 40 | 3. **Search_TrasparentEncryption**: when it is enabled, it encrypts transparently the bodies of all requests generated from the Scanner, the Repeater and the Intruder Burp Suite tools (but it can be edited to add other Burp Suite tools). It can be used by sending a request to the Intruder/Scanner/Repeater **with the body already decrypted**. In this way Burp Suite/the pentester can adds his payloads and the body will be transparently encrypted by Brida before the transmission to the backend 41 | 4. **Search_TrasparentDecryption**: when it is enabled, it decrypts transparently the bodies of all responses received by the Scanner, the Repeater and the Intruder Burp Suite tools (but it can be edited to add other Burp Suite tools). It can be used in conjunction with the previous plugin to have the body of the HTTP responses decrypted in the Scanner, in the Repeater and in the Intruder tools, allowing Burp Suite or the pentester to understand if the attack vector succeeded in an easy way 42 | 5. **Search_MessageEditorTabRequests**: it adds a message editor tab to HTTP requests, that shows the decrypted form of the body of the current HTTP message. If the tab is editable and the decrypted value is modified, Brida replace the original body with a new one containing the encrypted modified body 43 | 6. **Search_MessageEditorTabResponses**: it adds a message editor tab to HTTP responses, that shows the decrypted form of the body of the current HTTP message. If the tab is editable and the decrypted value is modified, Brida replace the original body with a new one containing the encrypted modified body 44 | 45 | ## Old Brida plugins 46 | 47 | In the **OldCompiledPlugins** folder a compiled version of a couple plugins has been supplied (both source code and builds). Brida from version **0.4** has the **Custom plugins** tab that can be used to create Brida plugins directly from the Brida graphical interfaces, without the need to code. In the previous versions of Brida it was necessary to code Brida plugins (in Java or Python). It is still possible to code Brida plugins, feature that can be useful in complex scenarios in which we need to code specific behaviours. 48 | 49 | ## Disclaimer 50 | 51 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Demo/iOS/dbBackendBridaDemo.db: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS brida_backend_db; 2 | 3 | USE brida_backend_db; 4 | 5 | CREATE TABLE IF NOT EXISTS products( 6 | id INT AUTO_INCREMENT PRIMARY KEY, 7 | name VARCHAR(200) NOT NULL, 8 | price FLOAT NOT NULL 9 | ) ENGINE=INNODB; 10 | 11 | CREATE TABLE IF NOT EXISTS device( 12 | id INT AUTO_INCREMENT PRIMARY KEY, 13 | device_id VARCHAR(200) NOT NULL, 14 | seed VARCHAR(200) NOT NULL, 15 | current_challenge VARCHAR(200) NOT NULL, 16 | content VARCHAR(200) NOT NULL, 17 | number_of_access INT NOT NULL 18 | ) ENGINE=INNODB; 19 | 20 | CREATE TABLE IF NOT EXISTS users( 21 | id INT AUTO_INCREMENT PRIMARY KEY, 22 | username VARCHAR(200) NOT NULL, 23 | password VARCHAR(200) NOT NULL 24 | ) ENGINE=INNODB; 25 | 26 | INSERT INTO products(name,price) VALUES ('coffee',5); 27 | INSERT INTO products(name,price) VALUES ('bread',1); 28 | INSERT INTO products(name,price) VALUES ('meat',3); 29 | 30 | INSERT INTO device(device_id,seed,current_challenge,content,number_of_access) VALUES ('axscfv1','qwssdedddsaa1','sdvgfvdvfd1','Test 1',0); 31 | INSERT INTO device(device_id,seed,current_challenge,content,number_of_access) VALUES ('fgdvcc2','fdxvfdvvaasx2','asdzxcfvvs2','Test 2',0); 32 | 33 | INSERT INTO users(username,password) VALUES ('admin','password123'); 34 | INSERT INTO users(username,password) VALUES ('mark','mark1'); -------------------------------------------------------------------------------- /Demo/iOS/exportedPluginsDemoIOS.csv: -------------------------------------------------------------------------------- 1 | 2;U2VhcmNoX0RlY3J5cHRDb250ZXh0;ZGVjcnlwdA==;3;RGVjcnlwdA==;4;;[];2;;[];[] 2 | 2;U2VhcmNoX0VuY3J5cHRDb250ZXh0;ZW5jcnlwdA==;3;RW5jcnlwdA==;4;;[];2;;[];[] 3 | 0;64,16,32;false;U2VhcmNoX1RyYW5zcGFyZW50RW5jcnlwdGlvbg==;ZW5jcnlwdA==;1;RW5jcnlwdA==;1;Y29udGVudD0=;5;Y29udGVudD0oLiop;[];3;Y29udGVudD0oLiop;[];[] 4 | 0;64,16,32;false;U2VhcmNoX1RyYW5zcGFyZW50RGVjcnlwdGlvbg==;ZGVjcnlwdA==;2;RW5jcnlwdA==;0;;3;;[];6;;[];[] 5 | 1;6;Y29udGVudD0oLiop;[];[];ZW5jcnlwdA==;[];U2VhcmNoX01lc3NhZ2VFZGl0b3JUYWJSZXF1ZXN0cw==;ZGVjcnlwdA==;1;RW5jcnlwdA==;1;Y29udGVudD0=;5;Y29udGVudD0oLiop;[];4;RGVjcnlwdGVk;[];[] 6 | 1;4;;[];[];ZW5jcnlwdA==;[];U2VhcmNoX01lc3NhZ2VFZGl0b3JUYWJSZXNwb25zZXM=;ZGVjcnlwdA==;2;RW5jcnlwdA==;0;;3;;[];4;RGVjcnlwdGVk;[];[] 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 federicodotta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Brida Logo 3 |

4 | 5 | # Brida 6 | 7 | [![](https://img.shields.io/github/stars/federicodotta/brida.svg?color=yellow)](https://github.com/federicodotta/brida) 8 | [![](https://img.shields.io/github/forks/federicodotta/brida.svg?color=green)](https://github.com/federicodotta/brida) 9 | [![](https://img.shields.io/github/issues-raw/federicodotta/brida.svg?color=red)](https://github.com/federicodotta/brida/issues) 10 | [![](https://img.shields.io/badge/license-MIT%20License-red.svg?color=lightgray)](https://opensource.org/licenses/MIT) 11 | [![](https://img.shields.io/badge/twitter-apps3c-blue.svg)](https://twitter.com/apps3c) 12 | 13 | Brida is a Burp Suite Extension that, working as a bridge between [Burp Suite](https://portswigger.net/burp/) and [Frida](https://www.frida.re/), lets you use and manipulate applications’ own methods while tampering the traffic exchanged between the applications and their back-end services/servers. It supports all platforms supported by Frida (Windows, macOS, Linux, iOS, Android, and QNX). 14 | 15 | # Brida Idea 16 | This idea is a need that is born during the analysis of some mobile application that use strong symmetric cryptography using random keys, without knowing the correct secret all data was not modifiable via Burp neither with a custom plugin. More generally, applications' logic could be based on cryptographic tokens, it could use a complex challenge-response algorithm as well, and so on. How can we tamper the messages? Most of the times the only viable approach is to decompile/disassemble the application, identify the functions or methods we’re interested in AND re-implement them. This approach is obviously time consuming and not always really viable: i.e. the generation of tokens and/or the encryption routines could be based on cryptographic material strictly tied to the device (state) or stored inside protected areas and thus not directly accessible... That’s when Brida comes in handy: instead of trying to extract keys/certificates and re-writing the routines we’re interested in, why don’t we let the application do the dirty work for us? 17 | 18 | # Who should use Brida? 19 | 20 | Short answer is **everyone**! 21 | 22 | Long answer is that Brida is a **collection of tools**, some of them created to speed-up everyday mobile assessments and to help new Frida users, while others aimed at addressing very complex situations, in which Brida can really make the difference. 23 | 24 | Tools that can be valuable to handle and speed-up everyday mobile assessments are the following ones: 25 | - Many Frida hooks for common tasks, directly callable from the GUI of the tool. These scripts include the most recent hooks for Android and iOS platforms to bypass and inspect security features 26 | - An analysis tab, in which you have a tree representation of the binary (Java/OBJC classes and methods, imports/exports) and from which you can graphically add inspection hooks (that print arguments and return value every time that the hooked function is executed) and tamper hooks (that dynamically change the return value of the hooked function every time that it is executed) 27 | - An integrated JS editor with JavaScript syntax highlighting, in order to be able to add your own Frida exports and Frida hooks directly from Burp Suite 28 | 29 | However, **the idea that led to Brida creation was to help pentesters/reversers/hackers to analyze the webservices of target mobile applications where complex security features are in place to protect the confidentiality and integrity of the HTTP requests and responses**. Encryption, obfuscation, signatures routines executed on requests and responses can make the work on testers very difficult and time-consuming, because usually it is necessary to reverse the security mechanisms and to implement a Burp Suite plugin or an external tool that first decrypt/de-obfuscate the HTTP messages and then eventually encrypt/obfuscate/sign edited messages. The same applies also to some non-security scenarios, like for example if a custom binary protocol is used to format the body of HTTP requests/responses for interoperability or performance purposes. 30 | 31 | Brida tries to speed-up those procedures as much as possible, by limiting the reversing effort and by completely removing (in most situations) the developing one, by offering an engine that allows to graphically create custom plugins that inspect and edit HTTP requests and responses **using the same mobile functions used by the mobile application itself**, thanks to Frida. Taking as an example a mobile application that encrypt/decrypt all the requests and responses with a custom/unknown encryption algorithm, Brida allows to **graphically** create a simple custom plugin that decrypt the encrypted HTTP messages using directly the mobile code used by the mobile application to do the job. Another example can be a custom plugin that transparently update signatures of signed HTTP requests when are sent to the backend using the same mobile functionality used by the application itself. 32 | 33 | More in detail, Brida allows to graphically create the plugins that: 34 | - Process requests/responses that pass through every Burp Suite tool, in order to be able to encrypt/decrypt/resign elements of requests and responses using Frida exported functions 35 | - Add custom tab to Burp Suite request/response pane, in order to be able to decrypt/decode/process requests/responses (or portion of them) using Frida exported functions (and then encrypt/encode/process modifications and replacing the original request/response, if any) 36 | - Add custom context menu options to invoke Frida exported functions on requests and responses 37 | - Add buttons that invoke/enable Frida exported functions 38 | 39 | And if Brida custom plugin engine is not enough for our super-complex situations, it is also possible to write external Python/Java Burp Suite extensions that leave to Brida the task of executing the functions of the target mobile application on the data of the extension. Brida "Generate Stubs" tool generates the Java or Python code that can be pasted in external Python or Java extensions to use the Brida bridge. 40 | 41 | 42 | # Requirements 43 | In order to be able to use Brida, you need: 44 | 1. Burp Suite (1.X or 2.X) 45 | 2. Frida client 46 | 3. Pyro4 47 | 4. frida-compile (**!!! use version 10.2.5, last version of frida-compile do not work at the moment, debug in progress... !!!**) 48 | 5. A jailbroken iOS device/rooted Android device with frida-server running on it (or an application patched with the frida-gadget) 49 | 6. An application to analyze! :D 50 | 51 | # Installation from GitHub 52 | 1. Install Python 2.7 or Python 3, Pyro4 (pip install pyro4) and frida (pip install frida). python virtual environments are fully supported. 53 | 2. Install Node.js, npm and frida-compile (npm install frida-compile@9). At the moment there are issues with version 10 of frida-compile, but we are trying to solve them. 54 | 3. Download Burp Suite: http://portswigger.net/burp/download.html 55 | 4. Download the last release of Brida: https://github.com/federicodotta/Brida/releases 56 | 5. Open Burp -> Extender -> Extensions -> Add -> Choose BridaXX.jar file 57 | 58 | # Installation from Burp Suite BApp Store 59 | 1. Install Python 2.7 or Python 3, Pyro4 (pip install pyro4) and frida (pip install frida). python virtual environments are fully supported. 60 | 2. Install Node.js, npm and frida-compile (npm install frida-compile@10.2.5). At the moment there are issues with version 10 of frida-compile, but we are trying to solve them. 61 | 3. Download Burp Suite: http://portswigger.net/burp/download.html 62 | 4. Open Burp -> Extender -> BApp Store -> Brida, Burp to Frida bridge -> Install 63 | 64 | # Build 65 | You can build Brida using Maven. Brida uses a modified version of RSyntaxTextArea, that you can find in this [fork](https://github.com/federicodotta/RSyntaxTextArea). In order to be able to build Brida you have to download the [last release](https://github.com/federicodotta/RSyntaxTextArea/releases) of the modified version of RSyntaxTextArea or build it and then install it locally with Maven using the following parameters: 66 | 67 | - groupId: com.fifesoft 68 | - artifactId: rsyntaxtextarea 69 | - version: 2.6.1.edited 70 | 71 | # Documentation 72 | Installation and usage notes can be found in the **[Wiki page](https://github.com/federicodotta/Brida/wiki)**. 73 | 74 | The slides of our conference presented at **HackInBo 2017 Winter Edition** that describes the **first version** can be found at: 75 | - (ENG) https://www.hackinbo.it/slides/1508354139_HackInBo%202017%20Winter%20Edition%20-%20Federico%20Dotta%20-%20Advanced%20mobile%20penetration%20testing%20with%20Brida%20-%20141017.pdf 76 | 77 | The slides and the video of our conference presented at **Hack In The Box 2018 Amsterdam** that describes the new features of the **version 0.2** can be found at: 78 | - (ENG) https://conference.hitb.org/hitbsecconf2018ams/materials/D1T1%20-%20Federico%20Dotta%20and%20Piergiovanni%20Cipolloni%20-%20Brida%20When%20Burp%20Suite%20Meets%20Frida.pdf 79 | - (ENG) https://www.youtube.com/watch?v=wPepicuHDzs&t=18s 80 | 81 | The video of our conference presented at **Hack In Paris ~~2020~~ 2021** (postponed for the COVID-19 global situation) that describes the new features of the **version ~~0.4~~ 0.5** can be found at: 82 | - (ENG) https://www.youtube.com/watch?v=RawqXSslsQk&list=PLaS1tu_LcHA8WE8ITALpeCX7b07rOBZcj 83 | 84 | # Demo 85 | 86 | Two different demo applications can be found in the **[Demo](https://github.com/federicodotta/Brida/tree/master/Demo)** folder, one Android and one iOS. 87 | 88 | The demo folder contains also the Brida plugins that can be used to bypass the encryption mechanisms used by the apps! 89 | 90 | # Authors 91 | - Federico Dotta, Principal Security Analyst at HN Security 92 | - Piergiovanni Cipolloni, Principal Security Analyst at HN Security 93 | 94 | # Contributors 95 | - Maurizio Agazzini 96 | 97 | # Frida Scripts 98 | Brida uses a lot of Frida code for dynamic hooking and for binary inspection, based on the work of: 99 | - Marco Ivaldi 100 | - Maurizio Agazzini 101 | - Luca Baggio 102 | - Federico Dotta 103 | 104 | Furthermore, Brida integrates many Frida hooks developed by various authors to inspect/bypass many security features. A list of projects (I hope quite exhaustive) from which I took Frida code for the "Hooks and functions" section of Brida is (random order): 105 | - Piergiovanni Cipolloni - [Universal Android SSL Pinning Bypass with Frida](https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida/) 106 | - Mattia Vinci and Maurizio Agazzini - [Universal Android SSL Pinning Bypass 2](https://codeshare.frida.re/@sowdust/universal-android-ssl-pinning-bypass-2/) 107 | - Maurizio Siddu - [frida-multiple-unpinning](https://codeshare.frida.re/@akabe1/frida-multiple-unpinning/) 108 | - dzonerzy - [fridantiroot](https://codeshare.frida.re/@dzonerzy/fridantiroot/) 109 | - F-Secure Labs - [Android KeyStore Audit](https://github.com/FSecureLABS/android-keystore-audit) 110 | - F-Secure Labs - [needle](https://github.com/FSecureLABS/android-keystore-audit) 111 | - Alban Diquet - [SSL Kill Switch 2](https://github.com/nabla-c0d3/ssl-kill-switch2/) 112 | - dki - [ios10-ssl-bypass](https://codeshare.frida.re/@dki/ios10-ssl-bypass/) 113 | - macho_reverser - [iOS 12 SSL Bypass](https://github.com/machoreverser/Frida-Scripts/blob/master/ssl_bypass.js) 114 | - Chaitin Tech - [Passionfruit](https://github.com/chaitin/passionfruit) 115 | - lich0 - [dump ios](https://codeshare.frida.re/@lichao890427/dump-ios/) 116 | - ay-kay - [iOS DataProtection](https://codeshare.frida.re/@ay-kay/ios-dataprotection/) 117 | - neil-wu - [FridaSwiftDump](https://codeshare.frida.re/@neil-wu/fridaswiftdump/) 118 | 119 | # Screenshot 120 | ![Brida Screenshot](https://raw.githubusercontent.com/federicodotta/Brida/master/BridaScreen1.PNG) 121 | 122 | # MIT License 123 | Copyright (c) 2021 Brida 124 | 125 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 126 | 127 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 128 | 129 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 130 | -------------------------------------------------------------------------------- /images/Components1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Components1.PNG -------------------------------------------------------------------------------- /images/Components10.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Components10.PNG -------------------------------------------------------------------------------- /images/Components11.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Components11.PNG -------------------------------------------------------------------------------- /images/Components2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Components2.PNG -------------------------------------------------------------------------------- /images/Components3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Components3.PNG -------------------------------------------------------------------------------- /images/Components4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Components4.PNG -------------------------------------------------------------------------------- /images/Components5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Components5.PNG -------------------------------------------------------------------------------- /images/Components6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Components6.PNG -------------------------------------------------------------------------------- /images/Components7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Components7.PNG -------------------------------------------------------------------------------- /images/Components8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Components8.PNG -------------------------------------------------------------------------------- /images/Components9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Components9.PNG -------------------------------------------------------------------------------- /images/DebugExport1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/DebugExport1.PNG -------------------------------------------------------------------------------- /images/GenerateStub1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/GenerateStub1.PNG -------------------------------------------------------------------------------- /images/GenerateStub2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/GenerateStub2.PNG -------------------------------------------------------------------------------- /images/GraphicalAnalysis1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/GraphicalAnalysis1.PNG -------------------------------------------------------------------------------- /images/GraphicalAnalysis2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/GraphicalAnalysis2.PNG -------------------------------------------------------------------------------- /images/GraphicalAnalysis3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/GraphicalAnalysis3.PNG -------------------------------------------------------------------------------- /images/GraphicalAnalysis4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/GraphicalAnalysis4.PNG -------------------------------------------------------------------------------- /images/GraphicalAnalysis5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/GraphicalAnalysis5.PNG -------------------------------------------------------------------------------- /images/GraphicalAnalysis6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/GraphicalAnalysis6.PNG -------------------------------------------------------------------------------- /images/GraphicalAnalysis7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/GraphicalAnalysis7.PNG -------------------------------------------------------------------------------- /images/GraphicalAnalysis8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/GraphicalAnalysis8.PNG -------------------------------------------------------------------------------- /images/GraphicalAnalysis9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/GraphicalAnalysis9.PNG -------------------------------------------------------------------------------- /images/HooksAndFunctions1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/HooksAndFunctions1.PNG -------------------------------------------------------------------------------- /images/HooksAndFunctions2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/HooksAndFunctions2.PNG -------------------------------------------------------------------------------- /images/IContextMenu_Plugin_1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IContextMenu_Plugin_1.PNG -------------------------------------------------------------------------------- /images/IContextMenu_Plugin_10.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IContextMenu_Plugin_10.PNG -------------------------------------------------------------------------------- /images/IContextMenu_Plugin_2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IContextMenu_Plugin_2.PNG -------------------------------------------------------------------------------- /images/IContextMenu_Plugin_3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IContextMenu_Plugin_3.PNG -------------------------------------------------------------------------------- /images/IContextMenu_Plugin_4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IContextMenu_Plugin_4.PNG -------------------------------------------------------------------------------- /images/IContextMenu_Plugin_5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IContextMenu_Plugin_5.PNG -------------------------------------------------------------------------------- /images/IContextMenu_Plugin_6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IContextMenu_Plugin_6.PNG -------------------------------------------------------------------------------- /images/IContextMenu_Plugin_7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IContextMenu_Plugin_7.PNG -------------------------------------------------------------------------------- /images/IContextMenu_Plugin_8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IContextMenu_Plugin_8.PNG -------------------------------------------------------------------------------- /images/IContextMenu_Plugin_9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IContextMenu_Plugin_9.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_0.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_0.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_1.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_10.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_10.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_11.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_11.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_2.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_3.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_4.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_5.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_6.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_7.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_8.PNG -------------------------------------------------------------------------------- /images/IHttpListener_Plugin_9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IHttpListener_Plugin_9.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_1.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_10.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_10.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_11.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_11.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_12.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_12.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_13.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_13.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_14.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_14.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_15.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_15.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_16.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_16.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_17.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_17.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_2.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_3.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_4.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_5.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_6.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_7.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_8.PNG -------------------------------------------------------------------------------- /images/IMessageEditorTab_Plugin_9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/IMessageEditorTab_Plugin_9.PNG -------------------------------------------------------------------------------- /images/JButton_Plugin_1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JButton_Plugin_1.PNG -------------------------------------------------------------------------------- /images/JButton_Plugin_2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JButton_Plugin_2.PNG -------------------------------------------------------------------------------- /images/JButton_Plugin_3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JButton_Plugin_3.PNG -------------------------------------------------------------------------------- /images/JButton_Plugin_4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JButton_Plugin_4.PNG -------------------------------------------------------------------------------- /images/JButton_Plugin_5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JButton_Plugin_5.PNG -------------------------------------------------------------------------------- /images/JButton_Plugin_6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JButton_Plugin_6.PNG -------------------------------------------------------------------------------- /images/JButton_Plugin_7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JButton_Plugin_7.PNG -------------------------------------------------------------------------------- /images/JButton_Plugin_8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JButton_Plugin_8.PNG -------------------------------------------------------------------------------- /images/JButton_Plugin_9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JButton_Plugin_9.PNG -------------------------------------------------------------------------------- /images/JsEditor1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JsEditor1.PNG -------------------------------------------------------------------------------- /images/JsEditor2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JsEditor2.PNG -------------------------------------------------------------------------------- /images/JsEditor3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JsEditor3.PNG -------------------------------------------------------------------------------- /images/JsEditor4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JsEditor4.PNG -------------------------------------------------------------------------------- /images/JsEditor5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/JsEditor5.PNG -------------------------------------------------------------------------------- /images/Start1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Start1.png -------------------------------------------------------------------------------- /images/Start2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Start2.png -------------------------------------------------------------------------------- /images/Start3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Start3.png -------------------------------------------------------------------------------- /images/Start4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Start4.png -------------------------------------------------------------------------------- /images/Start5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Start5.png -------------------------------------------------------------------------------- /images/Start6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/federicodotta/Brida/ab8315dcdb88059edfd50538011edf49927ab000/images/Start6.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.fd 6 | Brida 7 | 0.5 8 | jar 9 | 10 | Brida 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | maven-assembly-plugin 23 | 24 | 25 | 26 | true 27 | 28 | 29 | jar-with-dependencies 30 | 31 | 32 | 33 | 34 | true 35 | 36 | 37 | 38 | 1.8 39 | 1.8 40 | 41 | 42 | 43 | 44 | 45 | make-assembly 46 | package 47 | 48 | single 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | . 59 | 60 | res/*.* 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | net.portswigger.burp.extender 71 | burp-extender-api 72 | 1.7.22 73 | 74 | 75 | 76 | 77 | org.apache.commons 78 | commons-lang3 79 | 3.7 80 | 81 | 82 | 83 | 84 | commons-codec 85 | commons-codec 86 | 1.14 87 | 88 | 89 | 90 | 91 | org.json 92 | json 93 | 20171018 94 | 95 | 96 | 97 | 98 | net.razorvine 99 | pyrolite 100 | 4.30 101 | 102 | 103 | 104 | 105 | net.razorvine 106 | serpent 107 | 1.23 108 | 109 | 110 | 111 | 112 | com.fifesoft 113 | rsyntaxtextarea 114 | 2.6.1.edited 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /res/brida.js: -------------------------------------------------------------------------------- 1 | const androidpinningwithca1 = require("./androidDefaultHooks.js").androidpinningwithca1 2 | const androidpinningwithoutca1 = require("./androidDefaultHooks.js").androidpinningwithoutca1 3 | const androidrooting1 = require("./androidDefaultHooks.js").androidrooting1 4 | const androidfingerprintbypass1 = require("./androidDefaultHooks.js").androidfingerprintbypass1 5 | const androidfingerprintbypass2hook = require("./androidDefaultHooks.js").androidfingerprintbypass2hook 6 | const androidfingerprintbypass2function = require("./androidDefaultHooks.js").androidfingerprintbypass2function 7 | const tracekeystore = require("./androidDefaultHooks.js").tracekeystore 8 | const listaliasesstatic = require("./androidDefaultHooks.js").listaliasesstatic 9 | const listaliasesruntime = require("./androidDefaultHooks.js").listaliasesruntime 10 | const dumpcryptostuff = require("./androidDefaultHooks.js").dumpcryptostuff 11 | const okhttphostnameverifier = require("./androidDefaultHooks.js").okhttphostnameverifier 12 | const ios10pinning = require("./iosDefaultHooks.js").ios10pinning 13 | const ios11pinning = require("./iosDefaultHooks.js").ios11pinning 14 | const ios12pinning = require("./iosDefaultHooks.js").ios12pinning 15 | const ios13pinning = require("./iosDefaultHooks.js").ios13pinning 16 | const iosbypasstouchid = require("./iosDefaultHooks.js").iosbypasstouchid 17 | const iosjailbreak = require("./iosDefaultHooks.js").iosjailbreak 18 | const iosdumpkeychain = require("./iosDefaultHooks.js").iosdumpkeychain 19 | const iosdataprotectionkeys = require("./iosDefaultHooks.js").iosdataprotectionkeys 20 | const iosdumpcurrentencryptedapp = require("./iosDefaultHooks.js").iosdumpcurrentencryptedapp 21 | const dumpcryptostuffios = require("./iosDefaultHooks.js").dumpcryptostuffios 22 | const demangle = require("./iosDefaultHooks.js").demangle 23 | const getallclasses = require("./bridaFunctions").getallclasses 24 | const getallmodules = require("./bridaFunctions").getallmodules 25 | const getmoduleimports = require("./bridaFunctions").getmoduleimports 26 | const getmoduleexports = require("./bridaFunctions").getmoduleexports 27 | const getclassmethods = require("./bridaFunctions").getclassmethods 28 | const findobjcmethods = require("./bridaFunctions").findobjcmethods 29 | const findjavamethods = require("./bridaFunctions").findjavamethods 30 | const findimports = require("./bridaFunctions").findimports 31 | const findexports = require("./bridaFunctions").findexports 32 | const detachall = require("./bridaFunctions").detachall 33 | const trace = require("./bridaFunctions").trace 34 | const changereturnvalue = require("./bridaFunctions").changereturnvalue 35 | const getplatform = require("./bridaFunctions").getplatform 36 | 37 | // Brida User file: use this file to insert your Frida exports/hooks/functions. 38 | // Do not remove existing code (it is necessary for Brida) 39 | 40 | rpc.exports = { 41 | androidpinningwithca1, androidpinningwithoutca1, androidrooting1, 42 | androidfingerprintbypass1, androidfingerprintbypass2hook, 43 | androidfingerprintbypass2function, tracekeystore, listaliasesstatic, 44 | listaliasesruntime, dumpcryptostuff, okhttphostnameverifier, 45 | ios10pinning, ios11pinning, ios12pinning, ios13pinning, 46 | iosbypasstouchid, iosjailbreak, iosdumpkeychain, iosdataprotectionkeys, 47 | iosdumpcurrentencryptedapp, dumpcryptostuffios, demangle, 48 | getallclasses, getallmodules, getmoduleimports, getmoduleexports, 49 | getclassmethods, findobjcmethods, findjavamethods, findimports, 50 | findexports, detachall, trace, changereturnvalue, getplatform, 51 | 52 | // BE CAREFUL: Do not use uppercase characters in exported function name (automatically converted lowercase by Pyro) 53 | exportedfunction: function() { 54 | 55 | // Do stuff... 56 | // This functions can be called from custom plugins 57 | 58 | } 59 | 60 | // Put here the exported functions called by your custom plugins 61 | 62 | } 63 | 64 | // Put here you Frida hooks! 65 | 66 | //if(ObjC.available) { 67 | //if(Java.available) { 68 | 69 | // ... 70 | 71 | //} 72 | 73 | 74 | 75 | // Auxiliary functions - You can remove them if you don't need them! 76 | 77 | // Convert a hex string to a byte array 78 | function hexToBytes(hex) { 79 | for (var bytes = [], c = 0; c < hex.length; c += 2) 80 | bytes.push(parseInt(hex.substr(c, 2), 16)); 81 | return bytes; 82 | } 83 | 84 | // Convert a ASCII string to a hex string 85 | function stringToHex(str) { 86 | return str.split("").map(function(c) { 87 | return ("0" + c.charCodeAt(0).toString(16)).slice(-2); 88 | }).join(""); 89 | } 90 | 91 | // Convert a hex string to a ASCII string 92 | function hexToString(hexStr) { 93 | var hex = hexStr.toString();//force conversion 94 | var str = ''; 95 | for (var i = 0; i < hex.length; i += 2) 96 | str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); 97 | return str; 98 | } 99 | 100 | // Convert a byte array to a hex string 101 | function bytesToHex(bytes) { 102 | for (var hex = [], i = 0; i < bytes.length; i++) { 103 | hex.push((bytes[i] >>> 4).toString(16)); 104 | hex.push((bytes[i] & 0xF).toString(16)); 105 | } 106 | return hex.join(""); 107 | } 108 | 109 | // Native ArrayBuffer to Base64 110 | // https://gist.github.com/jonleighton/958841 111 | function base64ArrayBuffer(arrayBuffer) { 112 | var base64 = '' 113 | var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 114 | 115 | var bytes = new Uint8Array(arrayBuffer) 116 | var byteLength = bytes.byteLength 117 | var byteRemainder = byteLength % 3 118 | var mainLength = byteLength - byteRemainder 119 | 120 | var a, b, c, d 121 | var chunk 122 | 123 | // Main loop deals with bytes in chunks of 3 124 | for (var i = 0; i < mainLength; i = i + 3) { 125 | // Combine the three bytes into a single integer 126 | chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2] 127 | 128 | // Use bitmasks to extract 6-bit segments from the triplet 129 | a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18 130 | b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12 131 | c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6 132 | d = chunk & 63 // 63 = 2^6 - 1 133 | 134 | // Convert the raw binary segments to the appropriate ASCII encoding 135 | base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d] 136 | } 137 | 138 | // Deal with the remaining bytes and padding 139 | if (byteRemainder == 1) { 140 | chunk = bytes[mainLength] 141 | 142 | a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2 143 | 144 | // Set the 4 least significant bits to zero 145 | b = (chunk & 3) << 4 // 3 = 2^2 - 1 146 | 147 | base64 += encodings[a] + encodings[b] + '==' 148 | } else if (byteRemainder == 2) { 149 | chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1] 150 | 151 | a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10 152 | b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4 153 | 154 | // Set the 2 least significant bits to zero 155 | c = (chunk & 15) << 2 // 15 = 2^4 - 1 156 | 157 | base64 += encodings[a] + encodings[b] + encodings[c] + '=' 158 | } 159 | 160 | return base64 161 | } 162 | -------------------------------------------------------------------------------- /res/bridaFunctions.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getallclasses, getallmodules, getmoduleimports, getmoduleexports, 3 | getclassmethods, findobjcmethods, findjavamethods, findimports, 4 | findexports, detachall, trace, changereturnvalue, getplatform 5 | } 6 | 7 | function getallclasses() { 8 | var result = [] 9 | if (ObjC.available) { 10 | for (var className in ObjC.classes) { 11 | if (ObjC.classes.hasOwnProperty(className)) { 12 | result.push(className); 13 | } 14 | } 15 | } else if(Java.available) { 16 | Java.perform(function() { 17 | Java.enumerateLoadedClasses({ 18 | onMatch: function (className) { 19 | result.push(className); 20 | }, 21 | onComplete: function() { 22 | } 23 | }); 24 | }); 25 | } 26 | return result; 27 | } 28 | 29 | function getallmodules() { 30 | var results = {} 31 | var matches = Process.enumerateModules( { 32 | onMatch: function (module) { 33 | results[module['name']] = module['base']; 34 | }, 35 | onComplete: function () { 36 | } 37 | }); 38 | return results; 39 | } 40 | 41 | function getmoduleimports(importname) { 42 | var results = {} 43 | var matches = Module.enumerateImports(importname, { 44 | onMatch: function (module) { 45 | results[module['type'] + ": " + module['name']] = module['address']; 46 | }, 47 | onComplete: function () { 48 | } 49 | }); 50 | return results; 51 | } 52 | 53 | function getmoduleexports(exportname) { 54 | var results = {} 55 | var matches = Module.enumerateExports(exportname, { 56 | onMatch: function (module) { 57 | results[module['type'] + ": " + module['name']] = module['address']; 58 | }, 59 | onComplete: function () { 60 | } 61 | }); 62 | return results; 63 | } 64 | 65 | function getclassmethods(classname) { 66 | var results = {} 67 | if (ObjC.available) { 68 | var resolver = new ApiResolver("objc"); 69 | var matches = resolver.enumerateMatches("*[" + classname + " *]", { 70 | onMatch: function (match) { 71 | results[match['name']] = match['address']; 72 | }, 73 | onComplete: function () { 74 | } 75 | }); 76 | } else if(Java.available) { 77 | Java.perform(function() { 78 | results = getJavaMethodArgumentTypes(classname); 79 | }); 80 | } 81 | return results; 82 | } 83 | 84 | function findjavamethods(searchstring) { 85 | var results = {} 86 | if(Java.available) { 87 | Java.perform(function() { 88 | var groups = [] 89 | groups.push(Java.enumerateMethods('*' + searchstring + '*!*/s')) 90 | groups.push(Java.enumerateMethods('*!*' + searchstring + '*/s')) 91 | groups.forEach(g => { 92 | g.forEach(classLoader => { 93 | classLoader.classes.forEach(c => { 94 | var className = c.name; 95 | c.methods.forEach(m => { 96 | var methodSignature = className + "!" + m; 97 | results[methodSignature] = null; 98 | }); 99 | }); 100 | }); 101 | }); 102 | }); 103 | } 104 | return results; 105 | } 106 | 107 | 108 | 109 | function findobjcmethods(searchstring) { 110 | var results = {} 111 | var resolver = new ApiResolver("objc"); 112 | var matches = resolver.enumerateMatches("*[*" + searchstring + "* *]", { 113 | onMatch: function (match) { 114 | results[match['name']] = match['address']; 115 | }, 116 | onComplete: function () { 117 | } 118 | }); 119 | matches = resolver.enumerateMatches("*[* *" + searchstring + "*]", { 120 | onMatch: function (match) { 121 | results[match['name']] = match['address']; 122 | }, 123 | onComplete: function () { 124 | } 125 | }); 126 | return results; 127 | } 128 | 129 | function findimports(searchstring) { 130 | var results = {} 131 | var resolver = new ApiResolver("module"); 132 | var matches = resolver.enumerateMatches("imports:*" + searchstring + "*!*", { 133 | onMatch: function (match) { 134 | results[match['name']] = match['address']; 135 | }, 136 | onComplete: function () { 137 | } 138 | }); 139 | matches = resolver.enumerateMatches("imports:*!*" + searchstring + "*", { 140 | onMatch: function (match) { 141 | results[match['name']] = match['address']; 142 | }, 143 | onComplete: function () { 144 | } 145 | }); 146 | return results; 147 | } 148 | 149 | function findexports(searchstring) { 150 | var results = {} 151 | var resolver = new ApiResolver("module"); 152 | var matches = resolver.enumerateMatches("exports:*" + searchstring + "*!*", { 153 | onMatch: function (match) { 154 | results[match['name']] = match['address']; 155 | }, 156 | onComplete: function () { 157 | } 158 | }); 159 | matches = resolver.enumerateMatches("exports:*!*" + searchstring + "*", { 160 | onMatch: function (match) { 161 | results[match['name']] = match['address']; 162 | }, 163 | onComplete: function () { 164 | } 165 | }); 166 | return results; 167 | } 168 | 169 | function detachall() { 170 | Interceptor.detachAll(); 171 | } 172 | 173 | // generic trace 174 | function trace(pattern,type,backtrace) { 175 | // SINGLE EXPORT (ALL EXPORT OF A MODULE CAN BE A MESS AND CRASH THE APP) 176 | if(type == "export") { 177 | var res = new ApiResolver("module"); 178 | pattern = "exports:" + pattern; 179 | var matches = res.enumerateMatchesSync(pattern); 180 | var targets = uniqBy(matches, JSON.stringify); 181 | targets.forEach(function(target) { 182 | traceModule(target.address, target.name, backtrace); 183 | }); 184 | //OBJC 185 | } else if(type.startsWith("objc")) { 186 | if (ObjC.available) { 187 | var res; 188 | if(type === "objc_class") { 189 | res = new ApiResolver("objc"); 190 | pattern = "*[" + pattern + " *]"; 191 | } else if(type === "objc_method") { 192 | res = new ApiResolver("objc"); 193 | } 194 | var matches = res.enumerateMatchesSync(pattern); 195 | var targets = uniqBy(matches, JSON.stringify); 196 | targets.forEach(function(target) { 197 | traceObjC(target.address, target.name,backtrace); 198 | }); 199 | } 200 | // ANDROID 201 | } else if(type.startsWith("java")) { 202 | if(Java.available) { 203 | Java.perform(function() { 204 | if(type === "java_class") { 205 | var methodsDictionary = getJavaMethodArgumentTypes(pattern); 206 | var targets = Object.keys(methodsDictionary); 207 | targets.forEach(function(targetMethod) { 208 | traceJavaMethod(targetMethod,backtrace); 209 | }); 210 | } else { 211 | traceJavaMethod(pattern,backtrace); 212 | } 213 | }); 214 | } 215 | } 216 | } 217 | 218 | function changereturnvalue(pattern, type, typeret, newret) { 219 | if(ObjC.available) { 220 | changeReturnValueIOS(pattern, type, typeret, newret); 221 | } else if(Java.available) { 222 | Java.perform(function() { 223 | changeReturnValueAndroid(pattern, type, typeret, newret); 224 | }); 225 | } else { 226 | changeReturnValueGeneric(pattern, type, typeret, newret); 227 | } 228 | } 229 | 230 | function getplatform() { 231 | 232 | if(Java.available) { 233 | return 0; 234 | } else if(ObjC.available){ 235 | return 1; 236 | } else { 237 | return 2; 238 | } 239 | 240 | } 241 | 242 | 243 | /* 244 | This method is used to get Java methods with arguments in bytecode syntex. By simply calling the getDeclaredMethods of a Java Class object 245 | and then calling toString on each Method object we do not get types in bytecode format. For example we get 'byte[]' instead of 246 | '[B'. This function uses overload object of frida to get types in correct bytecode form. 247 | */ 248 | function getJavaMethodArgumentTypes(classname) { 249 | if(Java.available) { 250 | var results = {}; 251 | Java.perform(function() { 252 | var hook = Java.use(classname); 253 | var res = hook.class.getDeclaredMethods(); 254 | res.forEach(function(s) { 255 | //console.log("s " + s); 256 | var targetClassMethod = parseJavaMethod(s.toString()); 257 | //console.log("targetClassMethod " + targetClassMethod); 258 | var delim = targetClassMethod.lastIndexOf("."); 259 | if (delim === -1) return; 260 | var targetClass = targetClassMethod.slice(0, delim) 261 | var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length) 262 | //console.log("targetClass " + targetClass); 263 | //console.log("targetMethod " + targetMethod); 264 | var hookClass = Java.use(targetClass); 265 | var classMethodOverloads = hookClass[targetMethod].overloads; 266 | classMethodOverloads.forEach(function(cmo) { 267 | // overload.argumentTypes is an array of objects representing the arguments. In the "className" field of each object there 268 | // is the bytecode form of the class of the current argument 269 | var argumentTypes = cmo.argumentTypes; 270 | var argumentTypesArray = [] 271 | argumentTypes.forEach(function(cmo) { 272 | argumentTypesArray.push(cmo.className); 273 | }); 274 | var argumentTypesString = argumentTypesArray.toString(); 275 | // overload.returnType.className contain the bytecode form of the class of the return value 276 | var currentReturnType = cmo.returnType.className; 277 | var newPattern = currentReturnType + " " + targetClassMethod + "(" + argumentTypesString + ")"; 278 | //console.log(newPattern); 279 | results[newPattern] = 0; 280 | }); 281 | hookClass.$dispose; 282 | }); 283 | hook.$dispose; 284 | }); 285 | return results; 286 | } 287 | } 288 | 289 | function changeReturnValueIOS(pattern, type, typeret, newret) { 290 | var res; 291 | if(type === "objc_method") { 292 | res = new ApiResolver("objc"); 293 | } else { 294 | // SINGLE EXPORT 295 | res = new ApiResolver("module"); 296 | pattern = "exports:" + pattern; 297 | } 298 | var matches = res.enumerateMatchesSync(pattern); 299 | var targets = uniqBy(matches, JSON.stringify); 300 | targets.forEach(function(target) { 301 | Interceptor.attach(target.address, { 302 | onEnter: function(args) { 303 | }, 304 | onLeave: function(retval) { 305 | if(typeret === "String") { 306 | var a1 = ObjC.classes.NSString.stringWithString_(newret); 307 | try { 308 | console.log("*** " + pattern + " Replacing " + ObjC.Object(retval) + " with " + a1); 309 | } catch(err) { 310 | console.log("*** " + pattern + " Replacing " + retval + " with " + a1); 311 | } 312 | retval.replace(a1); 313 | } else if(typeret === "Ptr") { 314 | console.log("*** " + pattern + " Replacing " + ptr(retval) + " with " + ptr(newret)); 315 | retval.replace(ptr(newret)); 316 | } else if(typeret === "Boolean") { 317 | if(newret === "true") { 318 | var toRet = 1; 319 | } else { 320 | var toRet = 0; 321 | } 322 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 323 | retval.replace(toRet); 324 | } else { 325 | console.log("*** " + pattern + " Replacing " + retval + " with " + newret); 326 | retval.replace(newret); 327 | } 328 | } 329 | }); 330 | }); 331 | console.log("*** Replacing return value of " + pattern + " with " + newret); 332 | } 333 | 334 | function changeReturnValueGeneric(pattern, type, typeret, newret) { 335 | var res = new ApiResolver("module"); 336 | pattern = "exports:" + pattern; 337 | var matches = res.enumerateMatchesSync(pattern); 338 | var targets = uniqBy(matches, JSON.stringify); 339 | targets.forEach(function(target) { 340 | Interceptor.attach(target.address, { 341 | onEnter: function(args) { 342 | }, 343 | onLeave: function(retval) { 344 | if(typeret === "Ptr") { 345 | console.log("*** " + pattern + " Replacing " + ptr(retval) + " with " + ptr(newret)); 346 | retval.replace(ptr(newret)); 347 | } else if(typeret === "Boolean") { 348 | if(newret === "true") { 349 | var toRet = 1; 350 | } else { 351 | var toRet = 0; 352 | } 353 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 354 | retval.replace(toRet); 355 | } else { 356 | console.log("*** " + pattern + " Replacing " + retval + " with " + newret); 357 | retval.replace(newret); 358 | } 359 | } 360 | }); 361 | }); 362 | console.log("*** Replacing return value of " + pattern + " with " + newret); 363 | } 364 | 365 | function changeReturnValueAndroid(pattern, type, typeret, newret) { 366 | if(type === "java_method") { 367 | var targetClassMethod = parseJavaMethod(pattern); 368 | //console.log(targetClassMethod); 369 | var argsTargetClassMethod = getJavaMethodArguments(pattern); 370 | //console.log(argsTargetClassMethod); 371 | var delim = targetClassMethod.lastIndexOf("."); 372 | if (delim === -1) return; 373 | var targetClass = targetClassMethod.slice(0, delim) 374 | var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length) 375 | //console.log(targetClass); 376 | //console.log(targetMethod); 377 | var hook = Java.use(targetClass); 378 | hook[targetMethod].overload.apply(hook[targetMethod],argsTargetClassMethod).implementation = function() { 379 | var retval = this[targetMethod].apply(this, arguments); 380 | var toRet = newret; 381 | if(typeret === "String") { 382 | var stringClass = Java.use("java.lang.String"); 383 | toRet = stringClass.$new(newret); 384 | } else if(typeret === "Ptr") { 385 | toRet = ptr(newret); 386 | } else if(typeret === "Boolean") { 387 | if(newret === "true") { 388 | toRet = true; 389 | } else { 390 | toRet = false; 391 | } 392 | } 393 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 394 | return toRet; 395 | } 396 | // SINGLE EXPORT 397 | } else { 398 | var res = new ApiResolver("module"); 399 | var pattern = "exports:" + pattern; 400 | var matches = res.enumerateMatchesSync(pattern); 401 | var targets = uniqBy(matches, JSON.stringify); 402 | targets.forEach(function(target) { 403 | Interceptor.attach(target.address, { 404 | onEnter: function(args) { 405 | }, 406 | onLeave: function(retval) { 407 | var toRet = newret; 408 | if(typeret === "String") { 409 | var stringClass = Java.use("java.lang.String"); 410 | var toRet = stringClass.$new(newret); 411 | } else if(typeret === "ptr") { 412 | toRet = ptr(newret); 413 | } else if(typeret === "Boolean") { 414 | if(newret === "true") { 415 | var toRet = 1; 416 | } else { 417 | var toRet = 0; 418 | } 419 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 420 | retval.replace(toRet); 421 | } 422 | console.log("*** " + pattern + " Replacing " + retval + " with " + toRet); 423 | retval.replace(toRet); 424 | } 425 | }); 426 | }); 427 | } 428 | console.log("*** Replacing return value of " + pattern + " with " + newret); 429 | } 430 | 431 | // trace ObjC methods 432 | function traceObjC(impl, name, backtrace) { 433 | console.log("*** Tracing " + name); 434 | Interceptor.attach(impl, { 435 | onEnter: function(args) { 436 | console.log("*** entered " + name); 437 | console.log("Caller: " + DebugSymbol.fromAddress(this.returnAddress)); 438 | // print args 439 | if (name.indexOf(":") !== -1) { 440 | console.log("Parameters:"); 441 | var par = name.split(":"); 442 | par[0] = par[0].split(" ")[1]; 443 | for (var i = 0; i < par.length - 1; i++) { 444 | printArg(par[i] + ": ", args[i + 2]); 445 | } 446 | } 447 | if(backtrace === "true") { 448 | console.log("Backtrace:\n\t" + Thread.backtrace(this.context, Backtracer.ACCURATE) 449 | .map(DebugSymbol.fromAddress).join("\n\t")); 450 | } 451 | }, 452 | onLeave: function(retval) { 453 | console.log("*** exiting " + name); 454 | console.log("Return value:"); 455 | printArg("retval: ", retval); 456 | } 457 | }); 458 | } 459 | 460 | 461 | 462 | // trace a specific Java Method 463 | function traceJavaMethod(pattern,backtrace) { 464 | var targetClassMethod = parseJavaMethod(pattern); 465 | //console.log(targetClassMethod); 466 | var argsTargetClassMethod = getJavaMethodArguments(pattern); 467 | //console.log(argsTargetClassMethod); 468 | var delim = targetClassMethod.lastIndexOf("."); 469 | if (delim === -1) return; 470 | var targetClass = targetClassMethod.slice(0, delim) 471 | var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length) 472 | var hook = Java.use(targetClass); 473 | //var overloadCount = hook[targetMethod].overloads.length; 474 | console.log("*** Tracing " + pattern); 475 | hook[targetMethod].overload.apply(hook[targetMethod],argsTargetClassMethod).implementation = function() { 476 | console.log("*** entered " + targetClassMethod); 477 | // print args 478 | if (arguments.length) console.log("Parameters:"); 479 | for (var j = 0; j < arguments.length; j++) { 480 | console.log("\targ[" + j + "]: " + arguments[j]); 481 | } 482 | // print backtrace 483 | if(backtrace === "true") { 484 | Java.perform(function() { 485 | var threadClass = Java.use("java.lang.Thread"); 486 | var currentThread = threadClass.currentThread(); 487 | var currentStackTrace = currentThread.getStackTrace(); 488 | console.log("Backtrace:"); 489 | currentStackTrace.forEach(function(st) { 490 | console.log("\t" + st.toString()); 491 | }); 492 | }); 493 | } 494 | // print retval 495 | var retval = this[targetMethod].apply(this, arguments); 496 | console.log("*** exiting " + targetClassMethod); 497 | console.log("Return value:"); 498 | console.log("\tretval: " + retval); 499 | return retval; 500 | } 501 | } 502 | 503 | // trace Module functions 504 | function traceModule(impl, name, backtrace) { 505 | console.log("*** Tracing " + name); 506 | Interceptor.attach(impl, { 507 | onEnter: function(args) { 508 | console.log("*** entered " + name); 509 | if(backtrace === "true") { 510 | console.log("Backtrace:\n\t" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n\t")); 511 | } 512 | }, 513 | onLeave: function(retval) { 514 | console.log("*** exiting " + name); 515 | console.log("Return value:"); 516 | if(ObjC.available) { 517 | printArg("retval: ", retval); 518 | } else { 519 | console.log("\tretval: ", retval); 520 | } 521 | } 522 | }); 523 | } 524 | 525 | // print helper 526 | function printArg(desc, arg) { 527 | if(arg != 0x0) { 528 | try { 529 | var objectArg = ObjC.Object(arg); 530 | console.log("\t(" + objectArg.$className + ") " + desc + objectArg.toString()); 531 | } catch(err2) { 532 | console.log("\t" + desc + arg); 533 | } 534 | } else { 535 | console.log("\t" + desc + "0x0"); 536 | } 537 | } 538 | 539 | // remove duplicates from array 540 | function uniqBy(array, key) { 541 | var seen = {}; 542 | return array.filter(function(item) { 543 | var k = key(item); 544 | return seen.hasOwnProperty(k) ? false : (seen[k] = true); 545 | }); 546 | } 547 | 548 | /* 549 | INPUT LIKE: public boolean a.b.functionName(java.lang.String) 550 | OUTPUT LIKE: a.b.functionName 551 | */ 552 | function parseJavaMethod(method) { 553 | var parSplit = method.split("("); 554 | var spaceSplit = parSplit[0].split(" "); 555 | return spaceSplit[spaceSplit.length - 1]; 556 | } 557 | 558 | //INPUT LIKE: public boolean a.b.functionName(java.lang.String,java.lang.String) 559 | //OUTPUT LIKE: ["java.lang.String","java.lang.String"] 560 | function getJavaMethodArguments(method) { 561 | var m = method.match(/.*\((.*)\).*/); 562 | if(m[1] !== "") { 563 | return m[1].split(","); 564 | } else { 565 | return []; 566 | } 567 | } 568 | 569 | -------------------------------------------------------------------------------- /res/bridaServicePyro.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import frida 3 | import codecs 4 | import Pyro4 5 | import sys 6 | 7 | #reload(sys) 8 | #sys.setdefaultencoding('utf-8') 9 | 10 | class Unbuffered(object): 11 | def __init__(self, stream): 12 | self.stream = stream 13 | def write(self, data): 14 | self.stream.write(data) 15 | self.stream.flush() 16 | def writelines(self, datas): 17 | self.stream.writelines(datas) 18 | self.stream.flush() 19 | def __getattr__(self, attr): 20 | return getattr(self.stream, attr) 21 | 22 | @Pyro4.expose 23 | class BridaServicePyro: 24 | def __init__(self, daemon): 25 | self.daemon = daemon 26 | 27 | def attach_application(self,pid,frida_script,device,host_port_device_id): 28 | 29 | self.frida_script = frida_script 30 | 31 | if pid.isnumeric(): 32 | self.pid = int(pid) 33 | else: 34 | self.pid = pid 35 | 36 | if device == 'remote': 37 | self.device = frida.get_remote_device() 38 | elif device == 'usb': 39 | self.device = frida.get_usb_device() 40 | elif device == 'local': 41 | self.device = frida.get_local_device() 42 | elif device == 'device': 43 | self.device = frida.get_device(host_port_device_id) 44 | else: 45 | self.device = frida.get_device_manager().add_remote_device(host_port_device_id) 46 | 47 | self.session = self.device.attach(self.pid) 48 | 49 | with codecs.open(self.frida_script, 'r', 'utf-8') as f: 50 | source = f.read() 51 | 52 | self.script = self.session.create_script(source) 53 | self.script.load() 54 | 55 | return 56 | 57 | def spawn_application(self,application_id,frida_script,device,host_port_device_id): 58 | 59 | self.application_id = application_id 60 | self.frida_script = frida_script 61 | 62 | if device == 'remote': 63 | self.device = frida.get_remote_device() 64 | elif device == 'usb': 65 | self.device = frida.get_usb_device() 66 | elif device == 'local': 67 | self.device = frida.get_local_device() 68 | elif device == 'device': 69 | self.device = frida.get_device(host_port_device_id) 70 | else: 71 | self.device = frida.get_device_manager().add_remote_device(host_port_device_id) 72 | 73 | self.pid = self.device.spawn([self.application_id]) 74 | 75 | # Spawn and launch a specified activity 76 | #self.pid = self.device.spawn([self.application_id],activity="com.test.myApp.myActivity") 77 | 78 | self.session = self.device.attach(self.pid) 79 | 80 | with codecs.open(self.frida_script, 'r', 'utf-8') as f: 81 | source = f.read() 82 | 83 | self.script = self.session.create_script(source) 84 | self.script.load() 85 | 86 | return 87 | 88 | def resume_application(self): 89 | 90 | self.device.resume(self.pid) 91 | 92 | return 93 | 94 | def reload_script(self): 95 | 96 | with codecs.open(self.frida_script, 'r', 'utf-8') as f: 97 | source = f.read() 98 | 99 | self.script = self.session.create_script(source) 100 | self.script.load() 101 | 102 | return 103 | 104 | def disconnect_application(self): 105 | 106 | self.device.kill(self.pid) 107 | return 108 | 109 | def detach_application(self): 110 | 111 | self.session.detach() 112 | return 113 | 114 | def callexportfunction(self, methodName, args): 115 | method_to_call = getattr(self.script.exports, methodName) 116 | 117 | # Take the Java list passed as argument and create a new variable list of argument 118 | # (necessary for bridge Python - Java, I think) 119 | s = [] 120 | for i in args: 121 | s.append(i) 122 | 123 | return_value = method_to_call(*s) 124 | return return_value 125 | 126 | @Pyro4.oneway 127 | def shutdown(self): 128 | print('shutting down...') 129 | self.daemon.shutdown() 130 | 131 | # Disable python buffering (cause issues when communicating with Java...) 132 | sys.stdout = Unbuffered(sys.stdout) 133 | sys.stderr = Unbuffered(sys.stderr) 134 | 135 | host = sys.argv[1] 136 | port = int(sys.argv[2]) 137 | daemon = Pyro4.Daemon(host=host,port=port) 138 | 139 | #daemon = Pyro4.Daemon(host='127.0.0.1',port=9999) 140 | bs = BridaServicePyro(daemon) 141 | uri = daemon.register(bs,objectId='BridaServicePyro') 142 | 143 | print("Ready.") 144 | daemon.requestLoop() 145 | -------------------------------------------------------------------------------- /src/main/java/burp/BridaButtonPlugin.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Base64; 5 | import java.util.List; 6 | 7 | import javax.swing.JPanel; 8 | 9 | public class BridaButtonPlugin extends CustomPlugin { 10 | 11 | private JPanel buttonPanel; 12 | private DefaultHook hookOrFunction; 13 | 14 | public BridaButtonPlugin(int platform, boolean isInterceptorHook, 15 | BurpExtender mainPlugin, String customPluginName, String customPluginExportedFunctionName, 16 | CustomPluginExecuteOnValues customPluginExecuteOn, String customPluginExecuteOnButtonName, 17 | CustomPluginParameterValues customPluginParameter, 18 | String customPluginParameterString, List customPluginParameterEncoding, 19 | CustomPluginFunctionOutputValues customPluginFunctionOutput, String customPluginFunctionOutputString, 20 | List customPluginOutputEncoding, 21 | List customPluginOutputDecoding) { 22 | super(mainPlugin, customPluginName, customPluginExportedFunctionName, customPluginExecuteOn, customPluginExecuteOnButtonName, 23 | null, null, customPluginParameter, 24 | customPluginParameterString, customPluginParameterEncoding, customPluginFunctionOutput, 25 | customPluginFunctionOutputString, customPluginOutputEncoding, customPluginOutputDecoding); 26 | 27 | if(isInterceptorHook) { 28 | // It is not possible to pass parameters to hooks 29 | hookOrFunction = new DefaultHook(customPluginExecuteOnButtonName,platform,customPluginExportedFunctionName,isInterceptorHook,new ArrayList(),null,false); 30 | } else if(customPluginParameter == CustomPlugin.CustomPluginParameterValues.POPUP) { 31 | hookOrFunction = new DefaultHook(customPluginExecuteOnButtonName,platform,customPluginExportedFunctionName,isInterceptorHook,new ArrayList(),customPluginParameterEncoding,true); 32 | } else { 33 | hookOrFunction = new DefaultHook(customPluginExecuteOnButtonName,platform,customPluginExportedFunctionName,isInterceptorHook,getParametersCustomPlugin(null,false),customPluginParameterEncoding,false); 34 | } 35 | this.setType(CustomPlugin.CustomPluginType.JBUTTON); 36 | 37 | } 38 | 39 | @Override 40 | public String exportPlugin() { 41 | 42 | String result = ""; 43 | 44 | result = result + getType().ordinal() + ";"; 45 | 46 | result = result + hookOrFunction.getOs() + ";"; 47 | result = result + hookOrFunction.isInterceptorHook() + ";"; 48 | 49 | result = result + Base64.getEncoder().encodeToString(getCustomPluginName().getBytes()) + ";"; 50 | result = result + Base64.getEncoder().encodeToString(getCustomPluginExportedFunctionName().getBytes()) + ";"; 51 | result = result + getCustomPluginExecuteOn().ordinal() + ";"; 52 | result = result + Base64.getEncoder().encodeToString(getCustomPluginExecuteOnContextName().getBytes()) + ";"; 53 | result = result + getCustomPluginParameter().ordinal() + ";"; 54 | result = result + Base64.getEncoder().encodeToString(getCustomPluginParameterString().getBytes()) + ";"; 55 | result = result + getCustomPluginParameterEncoding().toString() + ";"; 56 | result = result + getCustomPluginFunctionOutput().ordinal() + ";"; 57 | result = result + Base64.getEncoder().encodeToString(getCustomPluginFunctionOutputString().getBytes()) + ";"; 58 | result = result + getCustomPluginOutputEncoding().toString() + ";"; 59 | result = result + getCustomPluginOutputDecoding().toString(); 60 | 61 | return result; 62 | 63 | } 64 | 65 | @Override 66 | public void enable() { 67 | buttonPanel = getMainPlugin().addButtonToHooksAndFunctions(hookOrFunction); 68 | setOnOff(true); 69 | 70 | } 71 | 72 | @Override 73 | public void disable() { 74 | if(isOn()) { 75 | // Disabling can fail for hooks if application is started. In this case the plugin should remain enabled. 76 | if(getMainPlugin().removeButtonFromHooksAndFunctions(buttonPanel, hookOrFunction)) { 77 | setOnOff(false); 78 | } 79 | } 80 | } 81 | 82 | public DefaultHook getHookOrFunction() { 83 | return hookOrFunction; 84 | } 85 | 86 | public void setHookOrFunction(DefaultHook hookOrFunction) { 87 | this.hookOrFunction = hookOrFunction; 88 | } 89 | 90 | 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/burp/BridaContextMenuPlugin.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.awt.event.ActionEvent; 4 | import java.awt.event.ActionListener; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.Base64; 8 | import java.util.List; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | import javax.swing.JMenuItem; 12 | import javax.swing.JOptionPane; 13 | import javax.swing.JScrollPane; 14 | import javax.swing.JTextArea; 15 | import javax.swing.SwingUtilities; 16 | 17 | import org.apache.commons.lang3.ArrayUtils; 18 | 19 | public class BridaContextMenuPlugin extends CustomPlugin implements IContextMenuFactory { 20 | 21 | public IContextMenuInvocation currentInvocation; 22 | 23 | public BridaContextMenuPlugin(BurpExtender mainPlugin, String customPluginName, String customPluginExportedFunctionName, 24 | CustomPluginExecuteOnValues customPluginExecuteOn, String customPluginExecuteOnContextName, 25 | CustomPluginParameterValues customPluginParameter, 26 | String customPluginParameterString, List customPluginParameterEncoding, 27 | CustomPluginFunctionOutputValues customPluginFunctionOutput, String customPluginFunctionOutputString, 28 | List customPluginOutputEncoding, 29 | List customPluginOutputDecoding) { 30 | super(mainPlugin, customPluginName, customPluginExportedFunctionName, customPluginExecuteOn, customPluginExecuteOnContextName, 31 | null, null, customPluginParameter, 32 | customPluginParameterString, customPluginParameterEncoding, customPluginFunctionOutput, 33 | customPluginFunctionOutputString, customPluginOutputEncoding, customPluginOutputDecoding); 34 | 35 | this.setType(CustomPlugin.CustomPluginType.ICONTEXTMENU); 36 | 37 | } 38 | 39 | @Override 40 | public String exportPlugin() { 41 | 42 | String result = ""; 43 | 44 | result = result + getType().ordinal() + ";"; 45 | 46 | result = result + Base64.getEncoder().encodeToString(getCustomPluginName().getBytes()) + ";"; 47 | result = result + Base64.getEncoder().encodeToString(getCustomPluginExportedFunctionName().getBytes()) + ";"; 48 | result = result + getCustomPluginExecuteOn().ordinal() + ";"; 49 | result = result + Base64.getEncoder().encodeToString(getCustomPluginExecuteOnContextName().getBytes()) + ";"; 50 | result = result + getCustomPluginParameter().ordinal() + ";"; 51 | result = result + Base64.getEncoder().encodeToString(getCustomPluginParameterString().getBytes()) + ";"; 52 | result = result + getCustomPluginParameterEncoding().toString() + ";"; 53 | result = result + getCustomPluginFunctionOutput().ordinal() + ";"; 54 | result = result + Base64.getEncoder().encodeToString(getCustomPluginFunctionOutputString().getBytes()) + ";"; 55 | result = result + getCustomPluginOutputEncoding().toString() + ";"; 56 | result = result + getCustomPluginOutputDecoding().toString(); 57 | 58 | return result; 59 | 60 | } 61 | 62 | @Override 63 | public void enable() { 64 | getMainPlugin().callbacks.registerContextMenuFactory(this); 65 | setOnOff(true); 66 | } 67 | 68 | @Override 69 | public void disable() { 70 | getMainPlugin().callbacks.removeContextMenuFactory(this); 71 | setOnOff(false); 72 | } 73 | 74 | @Override 75 | public List createMenuItems(IContextMenuInvocation invocation) { 76 | 77 | if(invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST || 78 | invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_RESPONSE || 79 | invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST || 80 | invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_RESPONSE) { 81 | 82 | currentInvocation = invocation; 83 | 84 | List menu = new ArrayList(); 85 | 86 | JMenuItem itemBridaPlugin = new JMenuItem(getCustomPluginExecuteOnContextName()); 87 | itemBridaPlugin.addActionListener(new ActionListener() { 88 | public void actionPerformed(ActionEvent actionEvent) { 89 | executeAction(); 90 | } 91 | }); 92 | 93 | menu.add(itemBridaPlugin); 94 | 95 | return menu; 96 | 97 | } else { 98 | 99 | return null; 100 | 101 | } 102 | 103 | } 104 | 105 | public void executeAction() { 106 | 107 | // If all is enabled 108 | if(getMainPlugin().serverStarted && getMainPlugin().applicationSpawned) { 109 | 110 | List parameters; 111 | 112 | IHttpRequestResponse[] selectedItems = currentInvocation.getSelectedMessages(); 113 | byte selectedInvocationContext = currentInvocation.getInvocationContext(); 114 | 115 | byte[] selectedRequestOrResponse = null; 116 | boolean isRequest; 117 | 118 | if(selectedInvocationContext == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST || selectedInvocationContext == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST) { 119 | selectedRequestOrResponse = selectedItems[0].getRequest(); 120 | isRequest = true; 121 | } else { 122 | selectedRequestOrResponse = selectedItems[0].getResponse(); 123 | isRequest = false; 124 | } 125 | 126 | if(getCustomPluginParameter() == CustomPluginParameterValues.CONTEXT) { 127 | 128 | int[] selectedBounds = currentInvocation.getSelectionBounds(); 129 | byte[] selectedPortion = Arrays.copyOfRange(selectedRequestOrResponse, selectedBounds[0], selectedBounds[1]); 130 | parameters = new ArrayList(); 131 | parameters.add(encodeCustomPluginValue(selectedPortion,getCustomPluginParameterEncoding(), getMainPlugin()) ); 132 | 133 | } else { 134 | 135 | parameters = getParametersCustomPlugin(selectedRequestOrResponse,isRequest); 136 | 137 | } 138 | 139 | byte[] ret = callFrida(parameters); 140 | 141 | // DEBUG print 142 | printToExternalDebugFrame("*** START ***\n\n"); 143 | printToExternalDebugFrame("** Original " + (isRequest ? "request" : "response") + "\n"); 144 | printToExternalDebugFrame(new String(selectedRequestOrResponse)); 145 | printToExternalDebugFrame("\n\n"); 146 | if(parameters.size() > 0) { 147 | printToExternalDebugFrame("** Frida parameters (after encoding)\n"); 148 | for(int i=0;i customPluginTools; 13 | private boolean processOnlyInScope; 14 | 15 | public BridaHttpListenerPlugin(ArrayList customPluginTools, boolean processOnlyInScope, BurpExtender mainPlugin, 16 | String customPluginName, String customPluginExportedFunctionName, 17 | CustomPluginExecuteOnValues customPluginExecuteOn, String customPluginExecuteOnContextName, 18 | CustomPluginExecuteValues customPluginExecute, 19 | String customPluginExecuteString, CustomPluginParameterValues customPluginParameter, 20 | String customPluginParameterString, List customPluginParameterEncoding, 21 | CustomPluginFunctionOutputValues customPluginFunctionOutput, String customPluginFunctionOutputString, 22 | List customPluginOutputEncoding, 23 | List customPluginOutputDecoding) { 24 | 25 | super(mainPlugin, customPluginName, customPluginExportedFunctionName, customPluginExecuteOn, customPluginExecuteOnContextName, 26 | customPluginExecute, customPluginExecuteString, customPluginParameter, 27 | customPluginParameterString, customPluginParameterEncoding, customPluginFunctionOutput, 28 | customPluginFunctionOutputString, customPluginOutputEncoding, customPluginOutputDecoding); 29 | 30 | this.customPluginTools = customPluginTools; 31 | this.processOnlyInScope = processOnlyInScope; 32 | 33 | this.setType(CustomPlugin.CustomPluginType.IHTTPLISTENER); 34 | 35 | } 36 | 37 | @Override 38 | public String exportPlugin() { 39 | 40 | String result = ""; 41 | 42 | result = result + getType().ordinal() + ";"; 43 | 44 | String pluginTools = ""; 45 | for(int i=0;i 0) { 49 | pluginTools = pluginTools.substring(0,pluginTools.length()-1); 50 | } 51 | 52 | result = result + pluginTools + ";"; 53 | result = result + processOnlyInScope + ";"; 54 | 55 | result = result + Base64.getEncoder().encodeToString(getCustomPluginName().getBytes()) + ";"; 56 | result = result + Base64.getEncoder().encodeToString(getCustomPluginExportedFunctionName().getBytes()) + ";"; 57 | result = result + getCustomPluginExecuteOn().ordinal() + ";"; 58 | result = result + Base64.getEncoder().encodeToString(getCustomPluginExecuteOnContextName().getBytes()) + ";"; 59 | result = result + getCustomPluginExecute().ordinal() + ";"; 60 | result = result + Base64.getEncoder().encodeToString(getCustomPluginExecuteString().getBytes()) + ";"; 61 | result = result + getCustomPluginParameter().ordinal() + ";"; 62 | result = result + Base64.getEncoder().encodeToString(getCustomPluginParameterString().getBytes()) + ";"; 63 | result = result + getCustomPluginParameterEncoding().toString() + ";"; 64 | result = result + getCustomPluginFunctionOutput().ordinal() + ";"; 65 | result = result + Base64.getEncoder().encodeToString(getCustomPluginFunctionOutputString().getBytes()) + ";"; 66 | result = result + getCustomPluginOutputEncoding().toString() + ";"; 67 | result = result + getCustomPluginOutputDecoding().toString(); 68 | 69 | return result; 70 | 71 | } 72 | 73 | public void enable() { 74 | 75 | if(customPluginTools.contains(IBurpExtenderCallbacks.TOOL_PROXY) && customPluginTools.size() == 1) { 76 | // Process only proxy requests/responses 77 | getMainPlugin().callbacks.registerProxyListener(this); 78 | } else if(!customPluginTools.contains(IBurpExtenderCallbacks.TOOL_PROXY)) { 79 | // Process only NON proxy requests/responses 80 | getMainPlugin().callbacks.registerHttpListener(this); 81 | } else { 82 | getMainPlugin().callbacks.registerProxyListener(this); 83 | getMainPlugin().callbacks.registerHttpListener(this); 84 | } 85 | 86 | setOnOff(true); 87 | 88 | } 89 | 90 | public void disable() { 91 | 92 | if(customPluginTools.contains(IBurpExtenderCallbacks.TOOL_PROXY) && customPluginTools.size() == 1) { 93 | getMainPlugin().callbacks.removeProxyListener(this); 94 | } else if(!customPluginTools.contains(IBurpExtenderCallbacks.TOOL_PROXY)) { 95 | getMainPlugin().callbacks.removeHttpListener(this); 96 | } else { 97 | getMainPlugin().callbacks.removeProxyListener(this); 98 | getMainPlugin().callbacks.removeHttpListener(this); 99 | } 100 | 101 | setOnOff(false); 102 | } 103 | 104 | 105 | @Override 106 | public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { 107 | 108 | byte[] requestResponseBytes = (messageIsRequest ? messageInfo.getRequest() : messageInfo.getResponse()); 109 | 110 | boolean scopeCheck = true; 111 | if(processOnlyInScope) { 112 | scopeCheck = getMainPlugin().callbacks.isInScope(getMainPlugin().helpers.analyzeRequest(messageInfo).getUrl()); 113 | } 114 | 115 | if(scopeCheck && customPluginTools.contains(toolFlag) && isPluginEnabled(requestResponseBytes, messageIsRequest) && toolFlag != IBurpExtenderCallbacks.TOOL_PROXY) { 116 | 117 | executeBridaFunction(messageInfo, messageIsRequest); 118 | 119 | } 120 | 121 | } 122 | 123 | 124 | public ArrayList getCustomPluginTools() { 125 | return customPluginTools; 126 | } 127 | 128 | 129 | public void setCustomPluginTools(ArrayList customPluginTools) { 130 | this.customPluginTools = customPluginTools; 131 | } 132 | 133 | public boolean isProcessOnlyInScope() { 134 | return processOnlyInScope; 135 | } 136 | 137 | public void setProcessOnlyInScope(boolean processOnlyInScope) { 138 | this.processOnlyInScope = processOnlyInScope; 139 | } 140 | 141 | @Override 142 | public void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage message) { 143 | 144 | IHttpRequestResponse messageInfo = message.getMessageInfo(); 145 | byte[] requestResponseBytes = (messageIsRequest ? messageInfo.getRequest() : messageInfo.getResponse()); 146 | 147 | boolean scopeCheck = true; 148 | if(processOnlyInScope) { 149 | scopeCheck = getMainPlugin().callbacks.isInScope(getMainPlugin().helpers.analyzeRequest(messageInfo).getUrl()); 150 | } 151 | 152 | if(scopeCheck && customPluginTools.contains(IBurpExtenderCallbacks.TOOL_PROXY) && isPluginEnabled(requestResponseBytes, messageIsRequest)) { 153 | 154 | executeBridaFunction(messageInfo, messageIsRequest); 155 | 156 | } 157 | 158 | } 159 | 160 | public void executeBridaFunction(IHttpRequestResponse messageInfo, boolean messageIsRequest) { 161 | 162 | byte[] requestResponseBytes = (messageIsRequest ? messageInfo.getRequest() : messageInfo.getResponse()); 163 | 164 | // DEBUG print 165 | printToExternalDebugFrame("*** START ***\n\n"); 166 | printToExternalDebugFrame("** Original " + (messageIsRequest ? "request" : "response") + "\n"); 167 | printToExternalDebugFrame(new String(requestResponseBytes)); 168 | printToExternalDebugFrame("\n\n"); 169 | 170 | List parameters = getParametersCustomPlugin(requestResponseBytes,messageIsRequest); 171 | 172 | // DEBUG print 173 | if(parameters.size() > 0) { 174 | printToExternalDebugFrame("** Frida parameters (after encoding)\n"); 175 | for(int i=0;i customPlugins; 14 | 15 | public CustomPluginsTableModel() { 16 | customPlugins = new ArrayList(); 17 | } 18 | 19 | public List getCustomPlugins() { 20 | return customPlugins; 21 | } 22 | 23 | @Override 24 | public int getColumnCount() { 25 | return 8; 26 | } 27 | 28 | @Override 29 | public int getRowCount() { 30 | return customPlugins.size(); 31 | } 32 | 33 | @Override 34 | public Object getValueAt(int rowIndex, int columnIndex) { 35 | 36 | CustomPlugin currentPlugin = customPlugins.get(rowIndex); 37 | 38 | switch (columnIndex) { 39 | 40 | case 0: 41 | if(currentPlugin.isOn()) { 42 | return "Enabled"; 43 | } else { 44 | return "Disabled"; 45 | } 46 | case 1: 47 | return currentPlugin.getCustomPluginName(); 48 | case 2: 49 | return convertPluginType(currentPlugin.getType()); 50 | case 3: 51 | return currentPlugin.getCustomPluginExportedFunctionName(); 52 | case 4: 53 | if(currentPlugin.isOn()) { 54 | return new JButton("DISABLE"); 55 | } else { 56 | return new JButton("ENABLE"); 57 | } 58 | case 5: 59 | if(currentPlugin.getType() != CustomPlugin.CustomPluginType.JBUTTON) { 60 | return new JButton("OPEN DEBUG WINDOW"); 61 | } else { 62 | return null; 63 | } 64 | case 6: 65 | return new JButton("EDIT"); 66 | case 7: 67 | return new JButton("REMOVE"); 68 | default: 69 | return ""; 70 | 71 | } 72 | 73 | } 74 | 75 | public static String convertPluginType(CustomPlugin.CustomPluginType type) { 76 | switch(type) { 77 | case IHTTPLISTENER: 78 | return "IHttpListener"; 79 | case IMESSAGEEDITORTAB: 80 | return "IMessageEditorTab"; 81 | case ICONTEXTMENU: 82 | return "IContextMenu"; 83 | case JBUTTON: 84 | return "JButton"; 85 | default: 86 | return "ERROR"; 87 | } 88 | } 89 | 90 | @Override 91 | public String getColumnName(int columnIndex) { 92 | switch (columnIndex) { 93 | case 0: 94 | return "Status"; 95 | case 1: 96 | return "Plugin name"; 97 | case 2: 98 | return "Plugin type"; 99 | case 3: 100 | return "Frida export name"; 101 | case 4: 102 | return "Enable/Disable"; 103 | case 5: 104 | return "Debug"; 105 | case 6: 106 | return "Edit"; 107 | case 7: 108 | return "Remove"; 109 | default: 110 | return ""; 111 | } 112 | } 113 | 114 | @Override 115 | public Class getColumnClass(int columnIndex) { 116 | if(columnIndex < 4) 117 | return String.class; 118 | else 119 | return JButton.class; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/burp/DefaultHook.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.util.List; 4 | 5 | public class DefaultHook { 6 | 7 | private String name; 8 | private int os; 9 | private String fridaExportName; 10 | private boolean isInterceptorHook; 11 | private boolean isEnabled; 12 | private List parameters; 13 | private List parametersEncoding; 14 | private boolean popupParameters; 15 | 16 | public DefaultHook(String name, int os, String code, boolean isInterceptorHook, List parameters, List parametersEncoding, boolean popupParameters) { 17 | this.name = name; 18 | this.os = os; 19 | this.fridaExportName = code; 20 | this.isInterceptorHook = isInterceptorHook; 21 | this.isEnabled = false; 22 | this.parameters = parameters; 23 | this.parametersEncoding = parametersEncoding; 24 | this.popupParameters = popupParameters; 25 | } 26 | public boolean isInterceptorHook() { 27 | return isInterceptorHook; 28 | } 29 | public void setInterceptorHook(boolean isInterceptorHook) { 30 | this.isInterceptorHook = isInterceptorHook; 31 | } 32 | public boolean isEnabled() { 33 | return isEnabled; 34 | } 35 | public void setEnabled(boolean isEnabled) { 36 | this.isEnabled = isEnabled; 37 | } 38 | public String getName() { 39 | return name; 40 | } 41 | public void setName(String name) { 42 | this.name = name; 43 | } 44 | public int getOs() { 45 | return os; 46 | } 47 | public void setOs(int os) { 48 | this.os = os; 49 | } 50 | public String getFridaExportName() { 51 | return fridaExportName; 52 | } 53 | public void setFridaExportName(String fridaExportName) { 54 | this.fridaExportName = fridaExportName; 55 | } 56 | public List getParameters() { 57 | return parameters; 58 | } 59 | public void setParameters(List parameters) { 60 | this.parameters = parameters; 61 | } 62 | public boolean isPopupParameters() { 63 | return popupParameters; 64 | } 65 | public void setPopupParameters(boolean popupParameters) { 66 | this.popupParameters = popupParameters; 67 | } 68 | public List getParametersEncoding() { 69 | return parametersEncoding; 70 | } 71 | public void setParametersEncoding(List parametersEncoding) { 72 | this.parametersEncoding = parametersEncoding; 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/burp/TrapTableItem.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | public class TrapTableItem { 4 | 5 | private String hook; 6 | private String type; 7 | private String name; 8 | private boolean backtrace; 9 | private String returnValueType; 10 | private String newReturnValue; 11 | private DefaultHook defaultHook; 12 | 13 | public TrapTableItem(String hook, String type, String name, boolean backtrace, String returnValueType, String newReturnValue, DefaultHook defaultHook) { 14 | 15 | this.hook = hook; 16 | this.type = type; 17 | this.name = name; 18 | this.backtrace = backtrace; 19 | this.returnValueType = returnValueType; 20 | this.newReturnValue = newReturnValue; 21 | this.defaultHook = defaultHook; 22 | 23 | } 24 | 25 | public String getHook() { 26 | return hook; 27 | } 28 | 29 | public void setHook(String hook) { 30 | this.hook = hook; 31 | } 32 | 33 | public String getType() { 34 | return type; 35 | } 36 | 37 | public void setType(String type) { 38 | this.type = type; 39 | } 40 | 41 | public String getName() { 42 | return name; 43 | } 44 | 45 | public void setName(String name) { 46 | this.name = name; 47 | } 48 | 49 | public boolean hasBacktrace() { 50 | return backtrace; 51 | } 52 | 53 | public void setBacktrace(boolean backtrace) { 54 | this.backtrace = backtrace; 55 | } 56 | 57 | public String getReturnValueType() { 58 | return returnValueType; 59 | } 60 | 61 | public void setReturnValueType(String returnValueType) { 62 | this.returnValueType = returnValueType; 63 | } 64 | 65 | public String getNewReturnValue() { 66 | return newReturnValue; 67 | } 68 | 69 | public void setNewReturnValue(String newReturnValue) { 70 | this.newReturnValue = newReturnValue; 71 | } 72 | 73 | public DefaultHook getDefaultHook() { 74 | return defaultHook; 75 | } 76 | 77 | public void setDefaultHook(DefaultHook defaultHook) { 78 | this.defaultHook = defaultHook; 79 | } 80 | 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/burp/TrapTableModel.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.swing.JButton; 7 | import javax.swing.table.AbstractTableModel; 8 | 9 | public class TrapTableModel extends AbstractTableModel { 10 | 11 | private static final long serialVersionUID = 1L; 12 | 13 | List trappedMethods; 14 | 15 | public TrapTableModel() { 16 | trappedMethods = new ArrayList(); 17 | } 18 | 19 | public List getTrappedMethods() { 20 | return trappedMethods; 21 | } 22 | 23 | 24 | @Override 25 | public int getRowCount() { 26 | return trappedMethods.size(); 27 | } 28 | 29 | @Override 30 | public int getColumnCount() { 31 | return 9; 32 | } 33 | 34 | @Override 35 | public Object getValueAt(int rowIndex, int columnIndex) { 36 | 37 | TrapTableItem currentItem = trappedMethods.get(rowIndex); 38 | 39 | switch (columnIndex) { 40 | 41 | case 0: 42 | if(currentItem.getDefaultHook().isEnabled()) { 43 | return "Enabled"; 44 | } else { 45 | return "Disabled"; 46 | } 47 | case 1: 48 | return currentItem.getHook(); 49 | case 2: 50 | return currentItem.getType(); 51 | case 3: 52 | return currentItem.getName(); 53 | case 4: 54 | return currentItem.hasBacktrace(); 55 | case 5: 56 | return currentItem.getReturnValueType(); 57 | case 6: 58 | return currentItem.getNewReturnValue(); 59 | case 7: 60 | if(currentItem.getDefaultHook().isEnabled()) { 61 | return new JButton("DISABLE"); 62 | } else { 63 | return new JButton("ENABLE"); 64 | } 65 | case 8: 66 | return new JButton("REMOVE"); 67 | default: 68 | return ""; 69 | 70 | } 71 | 72 | } 73 | 74 | @Override 75 | public String getColumnName(int columnIndex) { 76 | switch (columnIndex) { 77 | case 0: 78 | return "Status"; 79 | case 1: 80 | return "Category"; 81 | case 2: 82 | return "Type"; 83 | case 3: 84 | return "Method/Class"; 85 | case 4: 86 | return "Backtrace"; 87 | case 5: 88 | return "Return value type"; 89 | case 6: 90 | return "New return value"; 91 | case 7: 92 | return "Enable/Disable"; 93 | case 8: 94 | return "Remove"; 95 | default: 96 | return ""; 97 | } 98 | } 99 | 100 | @Override 101 | public Class getColumnClass(int columnIndex) { 102 | switch (columnIndex) { 103 | case 0: 104 | return String.class; 105 | case 1: 106 | return String.class; 107 | case 2: 108 | return String.class; 109 | case 3: 110 | return String.class; 111 | case 4: 112 | return Boolean.class; 113 | case 5: 114 | return String.class; 115 | case 6: 116 | return String.class; 117 | case 7: 118 | return JButton.class; 119 | case 8: 120 | return JButton.class; 121 | default: 122 | return String.class; 123 | } 124 | } 125 | 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/burp/TreePopup.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import javax.swing.JMenu; 4 | import javax.swing.JMenuItem; 5 | import javax.swing.JPopupMenu; 6 | 7 | public class TreePopup extends JPopupMenu { 8 | 9 | private static final long serialVersionUID = 1L; 10 | 11 | public TreePopup(BurpExtender ex){ 12 | 13 | JMenuItem trapItem = new JMenuItem("Inspect!"); 14 | trapItem.setActionCommand("trap"); 15 | trapItem.addActionListener(ex); 16 | add(trapItem); 17 | 18 | JMenuItem trapWithBacktraceItem = new JMenuItem("Inspect with backtrace!"); 19 | trapWithBacktraceItem.setActionCommand("trapBacktrace"); 20 | trapWithBacktraceItem.addActionListener(ex); 21 | add(trapWithBacktraceItem); 22 | 23 | JMenu changeReturnValue = new JMenu("Change return value"); 24 | 25 | JMenuItem changeReturnValuePtr = new JMenuItem("ptr"); 26 | changeReturnValuePtr.setActionCommand("changeReturnValuePtr"); 27 | changeReturnValuePtr.addActionListener(ex); 28 | changeReturnValue.add(changeReturnValuePtr); 29 | 30 | if(ex.getPlatform() == BurpExtender.PLATFORM_ANDROID || ex.getPlatform() == BurpExtender.PLATFORM_IOS) { 31 | JMenuItem changeReturnValueString = new JMenuItem("String"); 32 | changeReturnValueString.setActionCommand("changeReturnValueString"); 33 | changeReturnValueString.addActionListener(ex); 34 | changeReturnValue.add(changeReturnValueString); 35 | } 36 | 37 | JMenuItem changeReturnValueInt = new JMenuItem("int"); 38 | changeReturnValueInt.setActionCommand("changeReturnValueInt"); 39 | changeReturnValueInt.addActionListener(ex); 40 | changeReturnValue.add(changeReturnValueInt); 41 | 42 | JMenuItem changeReturnValueBoolean = new JMenuItem("boolean"); 43 | changeReturnValueBoolean.setActionCommand("changeReturnValueBoolean"); 44 | changeReturnValueBoolean.addActionListener(ex); 45 | changeReturnValue.add(changeReturnValueBoolean); 46 | 47 | add(changeReturnValue); 48 | 49 | if(ex.getPlatform() == BurpExtender.PLATFORM_IOS) { 50 | 51 | JMenuItem demangleItem = new JMenuItem("Demangle Swift name"); 52 | demangleItem.setActionCommand("demangle"); 53 | demangleItem.addActionListener(ex); 54 | add(demangleItem); 55 | 56 | } 57 | 58 | } 59 | 60 | } 61 | --------------------------------------------------------------------------------