Package: reflutter
Read more on the blog:https://swarm.ptsecurity.com/fork-bomb-for-flutter/This framework helps with Flutter apps reverse engineering using the patched version of the Flutter library which is already compiled and ready for app repacking. This library has snapshot deserialization process modified to allow you perform dynamic analysis in a convenient way.Key features:socket.ccis patched for traffic monitoring and interception;dart.ccis modified to print classes, functions and some fields;display absolute code offset for functionscontains minor changes for successfull compilation;if you would like to implement your own patches, there is manual Flutter code change is supported using specially craftedDockerfileSupported enginesAndroid: arm64, arm32;iOS: arm64;Release: Stable, BetaInstall# Linux, Windows, MacOS
pip3 install reflutter==0.7.8Usageimpact@f:~$refluttermain.apkPlease enter your Burp Suite IP: <input_ip>SnapshotHash: 8ee4ef7a67df9845fba331734198a953The resulting apk file: ./release.RE.apkPlease sign the apk fileConfigure Burp Suite proxy server to listen on *:8083Proxy Tab -> Options -> Proxy Listeners -> Edit -> Binding TabThen enable invisible proxying in Request Handling TabSupport Invisible Proxying -> trueimpact@f:~$refluttermain.ipaTraffic interceptionYou need to specify the IP of your Burp Suite Proxy Server located in the same network where the device with the flutter application is. Next, you should configure the Proxy inBurpSuite -> Listener Proxy -> Options tabAdd port:8083Bind to address:All interfacesRequest handling: Support invisible proxying =TrueYou don't need to install any certificates. On an Android device, you don't need root access as well. reFlutter also allows to bypass some of the flutter certificate pinning implementations.Usage on AndroidThe resulting apk must be aligned and signed. I useuber-apk-signerjava -jar uber-apk-signer.jar --allowResign -a release.RE.apk.
To see which code is loaded through DartVM, you need to run the application on the device. Note that you must manually find what_kDartIsolateSnapshotInstructions(ex. 0xB000 ) equals to using a binary search. reFlutter writes the dump to the root folder of the application and sets777permissions to the file and folder. You can pull the file with adb commandimpact@f:~$adb-dshell"cat /data/data/<PACKAGE_NAME>/dump.dart">dump.dartfile contentsLibrary:'package:anyapp/navigation/DeepLinkImpl.dart'Class:NavigationextendsObject{String*DeepUrl=anyapp://evil.com/ ;Function'Navigation.':constructor.(dynamic,dynamic,dynamic,dynamic)=>NavigationInteractor{CodeOffset:_kDartIsolateSnapshotInstructions+0x0000000000009270}Function'initDeepLinkHandle':.(dynamic)=>Future<void>*{CodeOffset:_kDartIsolateSnapshotInstructions+0x0000000000412fe8}Function'_navigateDeepLink@547106886':.(dynamic,dynamic,{dynamicnavigator})=>void{CodeOffset:_kDartIsolateSnapshotInstructions+0x0000000000002638}}Library:'package:anyapp/auth/navigation/AuthAccount.dart'Class:AuthAccountextendsAccount{PlainNotificationToken*_instance=sentinel;Function'getAuthToken':.(dynamic,dynamic,dynamic,dynamic)=>Future<AccessToken*>*{CodeOffset:_kDartIsolateSnapshotInstructions+0x00000000003ee548}Function'checkEmail':.(dynamic,dynamic)=>Future<bool*>*{CodeOffset:_kDartIsolateSnapshotInstructions+0x0000000000448a08}Function'validateRestoreCode':.(dynamic,dynamic,dynamic)=>Future<bool*>*{CodeOffset:_kDartIsolateSnapshotInstructions+0x0000000000412c34}Function'sendSmsRestorePassword':.(dynamic,dynamic)=>Future<bool*>*{CodeOffset:_kDartIsolateSnapshotInstructions+0x00000000003efb88}}Usage on iOSUse the IPA file created after the execution ofreflutter main.ipacommand. To see which code is loaded through DartVM, you need to run the application on the device. reFlutter will print the dump file path to the Xcode console logs with the reFlutter tagCurrent working dir: /private/var/mobile/Containers/Data/Application/<UUID>/dump.dartNext, you will need to pull the file from the deviceFridaThe resulting offset from the dump can be used in the fridascriptfrida -U -f <package> -l frida.js --no-pauseTo get value for_kDartIsolateSnapshotInstructionsyou can usereadelf -Ws libapp.soWhere is the value you need in theValuefieldTo DoDisplay absolute code offset for functions;Extract more strings and fields;Add socket patch;Extend engine support to Debug using Fork and Github Actions;Improve detection ofApp.frameworkandlibapp.soinside zip archiveBuild EngineThe engines are built usingreFlutterinGithub Actionsto build the desired version, commits and snapshot hashes are used from thistable.
The hash of the snapshot is extracted fromstorage.googleapis.com/flutter_infra_release/flutter/<hash>/android-arm64-release/linux-x64.zipreleaseCustom BuildIf you would like to implement your own patches, manual Flutter code change is supported using specially craftedDockergitclonehttps://github.com/Impact-I/reFlutter&&cdreFlutter
dockerbuild-treflutter-fDockerfile.Build command:dockerrun-it-v"$(pwd):/t"-eHASH_PATCH=<Snapshot_Hash>-eCOMMIT=<Engine_commit>reflutterExample:dockerrun-it-v"$(pwd):/t"-eHASH_PATCH=aa64af18e7d086041ac127cc4bc50c5e-eCOMMIT=d44b5a94c976fbb65815374f61ab5392a220b084reflutterLinux, WindowsEXAMPLE BUILD ANDROID ARM64:
docker run -e WAIT=300 -e x64=0 -e arm=0 -e HASH_PATCH=<Snapshot_Hash> -e COMMIT=<Engine_commit> --rm -iv${PWD}:/t reflutterFLAGS:
-e x64=0 <disables building for x64 architecture, use to reduce building time>
-e arm64=0 <disables building for arm64 architecture, use to reduce building time>
-e arm=0 <disables building for arm32 architecture, use to reduce building time>
-e WAIT=300 
-e HASH_PATCH=[Snapshot_Hash] 
-e COMMIT=[Engine_commit] <here you specify commit for your engine version, take it from enginehash.csv table or from flutter/engine repo>