Repository: ernw/AndroTickler Branch: master Commit: 501a29252979 Files: 174 Total size: 16.0 MB Directory structure: gitextract_nqfytlgy/ ├── .gradle/ │ ├── 8.8/ │ │ └── dependencies-accessors/ │ │ └── gc.properties │ ├── 8.9/ │ │ ├── dependencies-accessors/ │ │ │ └── gc.properties │ │ └── gc.properties │ ├── buildOutputCleanup/ │ │ └── cache.properties │ └── vcs-1/ │ └── gc.properties ├── LICENSE ├── README.md ├── Tickler.conf ├── build.gradle ├── libs/ │ ├── jars/ │ │ ├── aacommons-cli-1.3.jar │ │ ├── apktool.jar │ │ ├── apktool_2.2.2.jar │ │ ├── commons-cli-1.3.1.jar │ │ ├── commons-cli-1.3.jar │ │ ├── commons-codec-1.10.jar │ │ ├── commons-io-2.4.jar │ │ ├── commons-io-2.6.jar │ │ ├── jd-core-java-1.2.jar │ │ ├── json-simple-1.1.1.jar │ │ └── procyon-decompiler-0.5.30.jar │ └── notJars/ │ ├── Keystore/ │ │ └── Tickler.keystore │ ├── dex2jar-2.1/ │ │ ├── LICENSE.txt │ │ ├── NOTICE.txt │ │ ├── bin/ │ │ │ ├── dex-tools │ │ │ └── dex-tools.bat │ │ ├── d2j-apk-sign.bat │ │ ├── d2j-apk-sign.sh │ │ ├── d2j-asm-verify.bat │ │ ├── d2j-asm-verify.sh │ │ ├── d2j-baksmali.bat │ │ ├── d2j-baksmali.sh │ │ ├── d2j-class-version-switch.bat │ │ ├── d2j-class-version-switch.sh │ │ ├── d2j-decrypt-string.bat │ │ ├── d2j-decrypt-string.sh │ │ ├── d2j-dex-recompute-checksum.bat │ │ ├── d2j-dex-recompute-checksum.sh │ │ ├── d2j-dex-weaver.bat │ │ ├── d2j-dex-weaver.sh │ │ ├── d2j-dex2jar.bat │ │ ├── d2j-dex2jar.sh │ │ ├── d2j-dex2smali.bat │ │ ├── d2j-dex2smali.sh │ │ ├── d2j-jar-access.bat │ │ ├── d2j-jar-access.sh │ │ ├── d2j-jar-weaver.bat │ │ ├── d2j-jar-weaver.sh │ │ ├── d2j-jar2dex.bat │ │ ├── d2j-jar2dex.sh │ │ ├── d2j-jar2jasmin.bat │ │ ├── d2j-jar2jasmin.sh │ │ ├── d2j-jasmin2jar.bat │ │ ├── d2j-jasmin2jar.sh │ │ ├── d2j-smali.bat │ │ ├── d2j-smali.sh │ │ ├── d2j-std-apk.bat │ │ ├── d2j-std-apk.sh │ │ ├── d2j_invoke.bat │ │ ├── d2j_invoke.sh │ │ └── lib/ │ │ ├── ST4-4.0.8.jar │ │ ├── antlr-3.5.2.jar │ │ ├── antlr-runtime-3.5.2.jar │ │ ├── antlr4-4.5.jar │ │ ├── antlr4-runtime-4.5.jar │ │ ├── asm-debug-all-4.1.jar │ │ ├── asm-debug-all-5.0.3.jar │ │ ├── d2j-base-cmd-2.1-SNAPSHOT.jar │ │ ├── d2j-jasmin-2.1-SNAPSHOT.jar │ │ ├── d2j-smali-2.1-SNAPSHOT.jar │ │ ├── dex-ir-2.1-SNAPSHOT.jar │ │ ├── dex-reader-2.1-SNAPSHOT.jar │ │ ├── dex-reader-api-2.1-SNAPSHOT.jar │ │ ├── dex-tools-2.1-SNAPSHOT.jar │ │ ├── dex-translator-2.1-SNAPSHOT.jar │ │ ├── dex-writer-2.1-SNAPSHOT.jar │ │ ├── dx-1.7.jar │ │ ├── dx-23.0.0.jar │ │ ├── open-source-license.txt │ │ └── org.abego.treelayout.core-1.0.1.jar │ └── fridaScripts/ │ ├── enumerate_classes.py │ ├── get_attributes_output.js │ └── unpin_sslContext.py └── src/ └── main/ └── java/ ├── .gradle/ │ └── 2.10/ │ └── taskArtifacts/ │ └── cache.properties ├── actions/ │ ├── Comparer.java │ ├── Comparer_Old.java │ ├── Searcher.java │ └── Snapshots.java ├── apk/ │ ├── ApkSigner.java │ ├── ApkToolClass.java │ ├── ApkToolDude.java │ ├── AppBroker.java │ ├── Decompiler.java │ └── newApks/ │ ├── CreateApk.java │ ├── Debuggable.java │ ├── INewApk.java │ └── NougatMitM.java ├── attacks/ │ ├── ActivityStarter.java │ ├── Broadcaster.java │ ├── ProviderAttacker.java │ ├── StartAttack.java │ └── Starter.java ├── base/ │ ├── Base64Util.java │ ├── CopyUtil.java │ ├── DOMXMLReader.java │ ├── FileUtil.java │ ├── JsonParser.java │ ├── OtherUtil.java │ ├── SearchUtil.java │ ├── Tickler.java │ ├── TicklerGeneral.java │ └── XMLReader.java ├── cliGui/ │ ├── OutBut.java │ └── TicklerCLI.java ├── code/ │ ├── ClassExtras.java │ ├── ExtrasUtil.java │ └── JavaSqueezer.java ├── commandExec/ │ └── Commando.java ├── components/ │ ├── Action.java │ ├── Activity.java │ ├── Application.java │ ├── Category.java │ ├── DataUri.java │ ├── IActivityService.java │ ├── IComponent.java │ ├── Intent.java │ ├── Manifest.java │ ├── Permission.java │ ├── Provider.java │ ├── Receiver.java │ ├── Service.java │ ├── UsesPermission.java │ └── old/ │ ├── Action.java │ ├── Activity.java │ ├── Application.java │ ├── Category.java │ ├── DataUri.java │ ├── IActivityService.java │ ├── IComponent.java │ ├── Intent.java │ ├── Manifest.java │ ├── Permission.java │ ├── Provider.java │ ├── Receiver.java │ ├── Service.java │ ├── UsesPermission.java │ └── XMLReader.java ├── db/ │ └── DatabaseTester.java ├── device/ │ └── Packagez.java ├── docs/ │ └── helpMsg.txt ├── exceptions/ │ └── TNotFoundEx.java ├── frida/ │ ├── FridaBase.java │ ├── FridaCli.java │ ├── FridaEnumerateClasses.java │ ├── FridaGetArgsAndReturn.java │ ├── FridaInit.java │ ├── FridaJsAction.java │ ├── FridaJsScript.java │ ├── FridaPythonScript.java │ ├── FridaScript.java │ ├── FridaSetValue.java │ ├── FridaUnpinSslContext.java │ └── FridaVars.java ├── info/ │ ├── InfoGathering.java │ ├── InfoGatheringReporting.java │ └── ListComponents.java ├── initialization/ │ ├── TicklerChecks.java │ ├── TicklerConst.java │ └── TicklerVars.java ├── logs/ │ ├── LogReader.java │ └── LogReaderController.java └── manifest/ ├── ManifestAnalyzer.java ├── ManifestDealer.java └── handlers/ ├── DataUriHandler.java └── IntentHandler.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gradle/8.8/dependencies-accessors/gc.properties ================================================ ================================================ FILE: .gradle/8.9/dependencies-accessors/gc.properties ================================================ ================================================ FILE: .gradle/8.9/gc.properties ================================================ ================================================ FILE: .gradle/buildOutputCleanup/cache.properties ================================================ #Fri Mar 07 17:06:23 CET 2025 gradle.version=8.8 ================================================ FILE: .gradle/vcs-1/gc.properties ================================================ ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ **AndroTickler** ================= A java tool that helps to pentest Android apps faster, more easily and more efficiently. AndroTickler offers many features of information gathering, static and dynamic checks that cover most of the aspects of Android apps pentesting. It also offers several features that pentesters need during their pentests. AndroTickler also integrates with Frida to provide method tracing and manipulation. It was previously published under the name of **Tickler**. AndroTickler requires a linux host and a rooted Android device connected to its USB port. The tool does not install anything on the Android device, it only creates a *Tickler* directory on /sdcard . AndroTickler depends on Android SDK to run commands on the device and copy app's data to *TicklerWorkspace* directory on the host for further analysis. *TicklerWorkspace* is the working directory of AndroTickler and each app has a separate subdirectory in *TicklerWorkspace* which can contain the following (depending on user actions): - DataDir directory: a copy of the data directory of the app - extracted directory: Output of apktool on the app, contains smali code, resources, libraries...etc. - bgSnapshots directory: Contains background snapshots copied from the device. - images directory: contains any screenshots taken for the app. - JavaCode directory: Contains app's Java code decompiled by dex2jar and JD tools - logs directory: contains log files produced by -t -log, as explained below - transfers: files and directories copied from the device to the host using -copy2host - AndroidManifest.xml: The manifest file of the app as per apktool - base.apk: the APK file of the app, installed on the device - debuggable.apk: a debuggable version of the app, produced by -dbg *libs* directory and *Tickler.conf* configuration file exist in the same directory of the jar file. The configuration file sets the location of *TicklerDir* directory on the host and *Tickler* on /sdcard of the android device. If the configuration file does not exist or these 2 directories are not set, then default values will be used (Tickler\_workspace on the current directory and /sdcard/Tickler respectively). *Tickler_lib* directory contains some Java libraries and external tools used by AndroTickler such as apktool and dex2jar. AndroTickler highly depends on the following tools, so they should exist on your machine before using it: - Java 7 or higher - Android SDK tools (adb and friends) - sqlite3 Other tools are required for some features, but AndroTickler can still run without them: - Frida - jarsigner How to build it ================ 1) Build tool from code gradle build 2) Move AndroTickler.jar is to the same directory as Tickler_lib directory and Tickler.conf file (automatically created in build/libs) 3) Set dex2jar to executable (libs/notJars//dex2jar-2.1/d2j-dex2jar.sh and libs/notJars//dex2jar-2.1/d2j_invoke.sh) 4) Connect your Android device with the application-to-test installed on **Note**: If building the source code fails, you can download the latest compiled release from the [releases](https://github.com/ernw/AndroTickler/releases) tab The current version does the following: Command help ============= java -jar AndroTickler.jar -h Information gathering/Static analysis: ====================================== List installed Apps on the device: java -jar AndroTickler.jar -pkgs Searches for an app (package) installed on the device, whose package name contains the searchKey java -jar AndroTickler.jar -findPkg ### package without extra attributes java -jar AndroTickler.jar -pkg [other options] Any command with a -pkg option (whether used with any of the following options or not), does the following actions if they have not been done before: - Copies the app from the device - Extracts the Manifest file of the app - Decompiles the app to Java code using dex2jar and JD tools ### General Info java -jar AndroTickler.jar -pkg -info Returns the following information: - App's user ID - App's Directories path - If the app's code indicate usage of external storage - App's directories that already exist in External storage - Content URIs in the code - If the app is backable - If the app is debuggable - Data schemes (like iOS IPC) - The permissions it uses ## Code Squeezing java -jar AndroTickler.jar -pkg -squeeze [short | ] Fetches the following from the decompiled Java code of the app: - Log messages - Any indication of possible user credentials - Java comments - Used libs - URLs in code - Usage of shared preferences - Usage of external storage - Common components such as OkHttp and WebView Unsurprisingly, its output is usually huge, so it is recommended to redirect the command's output to a file *short* Squeezes only the decompiled code that belongs to the developer. For example, if an app has a package name of com.notEnaf.myapp, then *squeeze short* squeezes only the code in com/notEnaf directory. ** Squeezes the code only in *codeLocation* directory. Helpful to limit your search or squeeze the source code if available. ## Listing Components java -jar AndroTickler.jar -pkg -l [-exp] [-v] Lists all components of the app *-exp* Shows only exported components *-v* Gives more detailed information for each component: - Component type - Whether exported or not - Its intent filters - The tool checks the corresponding Java class to each component and returns all possible intent extras ### Listing any kind of components java -jar AndroTickler.jar -pkg -l [-act | -ser | -rec | -prov ] [-exp] [-v] - -act : activities - -ser : services - -rec: broadcast receivers - -prov: Content providers - -exp: show only exported components of any of the above type ## Databases java -jar AndroTickler.jar -pkg -db [|e|l|d] [nu] By default, all -db commands update the app's data storage directory on the host before running the check. *no attribute OR e* Tests whether the databases of the app are encrypted. It is the default action in case no option is given after -db flag. *l* Lists all databases of the app. Encrypted databases might not be detected. *d* Takes a sqlite dump of any of the unencrypted databases. *nu* noUpdate: runs any of the above options without updating the app's data directory on the host. ## Data Storage Directory Comparison java -jar AndroTickler.jar -pkg -diff [d|detailed] Copies the data storage directory of the app (to DataDirOld) then asks the user to do the action he wants and to press Enter when he's done. Then it copies the data storage directory again (to DataDir) and runs diff between them to show which files got added, deleted or modified. *d|detailed* Does the same as the normal -diff command, also shows what exactly changed in text files and unencrypted databases. ## Search ### Code java -jar AndroTickler.jar -pkg -sc [] Searches for the *key* in the following locations: - The decompiled Java code of the app - res/values/strings.xml - res/values/arrays.xml Search is case insensitive. ** Replaces the decompiled Java code location with the custom location. ### Storage java -jar AndroTickler.jar -pkg -sd Searches the Data storage directory of the app for the given key Tickling ========= Triggers components of the app, by all possible combinations of intents. For example, if an activity has an intent-filter of 2 possible actions and 3 data URI schemes, then AndroTickler will trigger this activity with all possible combinations of this intent. Additionally, AndroTickler captures the intent extras mentioned in the Java class corresponding to the component, assign them dummy values and add them to the possible intent combinations. Only extras of type boolean, string, int and float are supported. if the *-exp* option is used, then the components will be triggered without root privileges or any special permissions. If not, then the components will be trigged with root privileges. This helps to test the app in 2 different scenarios: against normal-privileged or high-privileged attackers. Before triggering components, AndroTickler prints all the commands to be executed. Then for each command, it triggers the component, prints the command then waits for the user. This gives the user enough time to do any extra checks after the command's execution. Before the user moves on to the next command, he's given the option to capture a screenshot of the device for PoC documentation. java -jar AndroTickler.jar -pkg -t [-all | -exp] [target] [-log] *target* as explained with list command, can be: - -act : activities. starts the (activity/activities) with all intent combinations as explained above - -ser : services. starts the service(s) with all intent combinations as explained above - -rec: broadcast receivers: sends all possible broadcast messages that would match the broadcast receiver(s) - -prov: Content providers: queries the content provider(s) if no value, then the target is all of the above *[-comp] * Specifies one component only. You can also use ** directly without -comp flag. *-exp* AndroTickler uses normal privileges to trigger only the exported targets. *-all* The default option. AndroTickler uses root privileges to trigger the exported targets *-log* Captures all logcat messages generated during the triggering session. Log file is saved in logs subdirectory. Frida: ====== Frida should be installed on your host machine. Also the location of Frida server on the Android device should be added to *Tickler.conf* file in the *Frida_server_path* entry ## Capture Arguments and return value java -jar AndroTickler.jar -pkg -frida vals [-reuse] Displays arguments and return value of this method (only primitive datatypes and String) *reuse* In case of vals and set options, Frida creates/updates a Frida script of that functionality. You can modify the created script as you want, then if you want to run it through AndroTickler, then use *-reuse* option so that it doesn't get overridden. ## Modify Arguments or Return Value java -jar AndroTickler.jar -pkg -frida set [-reuse] Sets the argument number *NumberOfArgToModify* to *newValue* (only primitive datatypes and String). *NumberOfArgToModify* starts with 0: First argument --> NumberOfArgToModify = 0, ...etc To modify return value --> set NumberOfArgToModify to *ret* ## Run JS Frida script java -jar AndroTickler.jar -pkg -frida script Runs a frida JS script located at *scriptPath* on your host Enumerate loaded classes: java -jar AndroTickler.jar -pkg -frida enum Other Features ================= ### Debuggable version java -jar AndroTickler.jar -pkg -dbg Creates a debuggable version of the app, which can be installed on the device and debugged using any external tool. AndroTickler comes with a keystore to sign the debuggable apk, but it requires *jarsigner* tool on the host. ### Custom version java -jar AndroTickler.jar -pkg -apk Builds an apk file from a directory, signs it and installs it. ### Background Snapshots java -jar AndroTickler.jar [-pkg ] [-bg|--bgSnapshots] Copies the background snapshots taken by the device (works with and without -pkg option) to *bgSnapshots* subdirectory. ### Copy files / directories Copy Data storage directory: java -jar AndroTickler.jar -pkg -dataDir [dest] Copies Data storage directory to DataDir *dest* Optional name of the destination directory, which will be located anyway at *transfers* sudirectory. Copy any file / directory: java -jar AndroTickler.jar -pkg -cp2host [dest] Copies files / directories from the android devices. - source_path is the absolute location of what you want to copy from the android device - dest: optional name of the destination directory, which will be located anyway at *transfers* sudirectory. If dest option is not given then the directory's name will be the timestamp of the transaction. ### Screenshot java -jar AndroTickler.jar [-pkg ] -screen - Captures the current screenshot of the device and saves them in *images* subdirectory - Works with or without the package flag ### Note For the options that do not require -pkg option, their data will be saved at *Tickler_Dir*/NoPackage Examples: ========= java -jar AndroTickler.jar -pkg -t -act -exp Triggers exported activities java -jar AndroTickler.jar -pkg -t -prov -log Queries all content providers and saves logcat messages until the tool stops execution java -jar AndroTickler.jar -pkg -t Triggers the component, type of triggering depends on the type of the component java -jar AndroTickler.jar -pkg de.not3naf.myApp -frida set de.not3naf.myApp.myActivity myMethod 2 0 "newValue" Hooks to the method "myMethod" that has 2 arguments and changes value of the first argument (args[0]) to "newValue" java -jar AndroTickler.jar -pkg de.not3naf.myApp -frida set de.not3naf.myApp.myActivity myMethod 2 2 false Hooks to the method "myMethod" that has 2 arguments and changes the return value to boolean false ================================================ FILE: Tickler.conf ================================================ Tickler_local_directory = Tickler_sdcard_directory = /sdcard/Tickler/ Frida_server_path = /data/local/tmp/frida-server ================================================ FILE: build.gradle ================================================ apply plugin: 'java' apply plugin: 'eclipse' repositories { jcenter() mavenCentral() } dependencies { compile 'org.slf4j:slf4j-api:1.7.13' compile files('libs/jars/commons-cli-1.3.jar') compile files('libs/jars/commons-io-2.4.jar') compile files('libs/jars/apktool_2.2.2.jar') compile files('libs/jars/commons-codec-1.10.jar') compile files('libs/jars/jd-core-java-1.2.jar') compile files('libs/jars/json-simple-1.1.1.jar') runtime files('libs/jars/commons-cli-1.3.jar') runtime fileTree(dir: 'libs/jars', include: '*.jar') } jar { baseName='AndroTickler' manifest { attributes( 'Class-Path': configurations.compile.collect { it.getName() }.join(' '), 'Main-Class': 'cliGui.TicklerCLI' ) } from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } } ================================================ FILE: libs/jars/apktool.jar ================================================ [File too large to display: 15.6 MB] ================================================ FILE: libs/notJars/dex2jar-2.1/LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: libs/notJars/dex2jar-2.1/NOTICE.txt ================================================ dex2jar - Tools to work with android .dex and java .class files Copyright (c) 2009-2014 Panxiaobo contributors - Bob Pan - Enea Stanzani - t3stwhat - paulhooijenga - yyjdelete - jcmdev0 ================================================ FILE: libs/notJars/dex2jar-2.1/bin/dex-tools ================================================ #!/usr/bin/env sh ############################################################################## ## ## dex-tools start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/.." >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="dex-tools" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and DEX_TOOLS_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/lib/ST4-4.0.8.jar:$APP_HOME/lib/org.abego.treelayout.core-1.0.1.jar:$APP_HOME/lib/antlr4-runtime-4.5.jar:$APP_HOME/lib/d2j-smali-2.1-SNAPSHOT.jar:$APP_HOME/lib/dex-writer-2.1-SNAPSHOT.jar:$APP_HOME/lib/antlr-runtime-3.5.2.jar:$APP_HOME/lib/asm-debug-all-5.0.3.jar:$APP_HOME/lib/dex-translator-2.1-SNAPSHOT.jar:$APP_HOME/lib/dex-reader-2.1-SNAPSHOT.jar:$APP_HOME/lib/dx-23.0.0.jar:$APP_HOME/lib/dex-reader-api-2.1-SNAPSHOT.jar:$APP_HOME/lib/antlr4-4.5.jar:$APP_HOME/lib/dex-tools-2.1-SNAPSHOT.jar:$APP_HOME/lib/d2j-jasmin-2.1-SNAPSHOT.jar:$APP_HOME/lib/dex-ir-2.1-SNAPSHOT.jar:$APP_HOME/lib/d2j-base-cmd-2.1-SNAPSHOT.jar:$APP_HOME/lib/antlr-3.5.2.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=$(save "$@") # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $DEX_TOOLS_OPTS -classpath "\"$CLASSPATH\"" com.googlecode.dex2jar.tools.BaseCmd "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/bin/dex-tools.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem dex-tools startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME%.. @rem Add default JVM options here. You can also use JAVA_OPTS and DEX_TOOLS_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\lib\ST4-4.0.8.jar;%APP_HOME%\lib\org.abego.treelayout.core-1.0.1.jar;%APP_HOME%\lib\antlr4-runtime-4.5.jar;%APP_HOME%\lib\d2j-smali-2.1-SNAPSHOT.jar;%APP_HOME%\lib\dex-writer-2.1-SNAPSHOT.jar;%APP_HOME%\lib\antlr-runtime-3.5.2.jar;%APP_HOME%\lib\asm-debug-all-5.0.3.jar;%APP_HOME%\lib\dex-translator-2.1-SNAPSHOT.jar;%APP_HOME%\lib\dex-reader-2.1-SNAPSHOT.jar;%APP_HOME%\lib\dx-23.0.0.jar;%APP_HOME%\lib\dex-reader-api-2.1-SNAPSHOT.jar;%APP_HOME%\lib\antlr4-4.5.jar;%APP_HOME%\lib\dex-tools-2.1-SNAPSHOT.jar;%APP_HOME%\lib\d2j-jasmin-2.1-SNAPSHOT.jar;%APP_HOME%\lib\dex-ir-2.1-SNAPSHOT.jar;%APP_HOME%\lib\d2j-base-cmd-2.1-SNAPSHOT.jar;%APP_HOME%\lib\antlr-3.5.2.jar @rem Execute dex-tools "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %DEX_TOOLS_OPTS% -classpath "%CLASSPATH%" com.googlecode.dex2jar.tools.BaseCmd %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable DEX_TOOLS_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%DEX_TOOLS_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-apk-sign.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.ApkSign %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-apk-sign.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.dex2jar.tools.ApkSign" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-asm-verify.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.AsmVerify %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-asm-verify.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.dex2jar.tools.AsmVerify" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-baksmali.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.d2j.smali.BaksmaliCmd %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-baksmali.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.d2j.smali.BaksmaliCmd" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-class-version-switch.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.ClassVersionSwitch %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-class-version-switch.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.dex2jar.tools.ClassVersionSwitch" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-decrypt-string.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.DecryptStringCmd %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-decrypt-string.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.dex2jar.tools.DecryptStringCmd" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-dex-recompute-checksum.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.DexRecomputeChecksum %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-dex-recompute-checksum.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.dex2jar.tools.DexRecomputeChecksum" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-dex-weaver.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.DexWeaverCmd %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-dex-weaver.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.dex2jar.tools.DexWeaverCmd" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-dex2jar.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.Dex2jarCmd %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-dex2jar.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.dex2jar.tools.Dex2jarCmd" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-dex2smali.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.d2j.smali.BaksmaliCmd %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-dex2smali.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.d2j.smali.BaksmaliCmd" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-jar-access.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.JarAccessCmd %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-jar-access.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.dex2jar.tools.JarAccessCmd" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-jar-weaver.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.JarWeaverCmd %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-jar-weaver.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.dex2jar.tools.JarWeaverCmd" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-jar2dex.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.Jar2Dex %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-jar2dex.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.dex2jar.tools.Jar2Dex" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-jar2jasmin.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.d2j.jasmin.Jar2JasminCmd %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-jar2jasmin.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.d2j.jasmin.Jar2JasminCmd" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-jasmin2jar.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.d2j.jasmin.Jasmin2JarCmd %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-jasmin2jar.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.d2j.jasmin.Jasmin2JarCmd" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-smali.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.d2j.smali.SmaliCmd %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-smali.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.d2j.smali.SmaliCmd" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-std-apk.bat ================================================ @echo off REM REM dex2jar - Tools to work with android .dex and java .class files REM Copyright (c) 2009-2013 Panxiaobo REM REM Licensed under the Apache License, Version 2.0 (the "License"); REM you may not use this file except in compliance with the License. REM You may obtain a copy of the License at REM REM http://www.apache.org/licenses/LICENSE-2.0 REM REM Unless required by applicable law or agreed to in writing, software REM distributed under the License is distributed on an "AS IS" BASIS, REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. REM See the License for the specific language governing permissions and REM limitations under the License. REM REM call d2j_invoke.bat to setup java environment @"%~dp0d2j_invoke.bat" com.googlecode.dex2jar.tools.StdApkCmd %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j-std-apk.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "com.googlecode.dex2jar.tools.StdApkCmd" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/d2j_invoke.bat ================================================ @echo off REM better invocation scripts for windows from lanchon, release in public domain. thanks! REM https://code.google.com/p/dex2jar/issues/detail?id=192 setlocal enabledelayedexpansion set LIB=%~dp0lib set CP= for %%X in ("%LIB%"\*.jar) do ( set CP=!CP!%%X; ) java -Xms512m -Xmx1024m -cp "%CP%" %* ================================================ FILE: libs/notJars/dex2jar-2.1/d2j_invoke.sh ================================================ #!/bin/sh # # dex2jar - Tools to work with android .dex and java .class files # Copyright (c) 2009-2013 Panxiaobo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # copy from $Tomcat/bin/startup.sh # resolve links - $0 may be a softlink PRG="$0" while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`/"$link" fi done PRGDIR=`dirname "$PRG"` # _classpath="." if [ `uname -a | grep -i -c cygwin` -ne 0 ]; then # Cygwin, translate the path for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath};`cygpath -w ${k}`" done else for k in "$PRGDIR"/lib/*.jar do _classpath="${_classpath}:${k}" done fi java -Xms512m -Xmx1024m -classpath "${_classpath}" "$@" ================================================ FILE: libs/notJars/dex2jar-2.1/lib/open-source-license.txt ================================================ ==== dx-*.jar Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0.html ==== antlr-*.jar [The BSD License] Copyright (c) 2003-2007, Terence Parr All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==== asm-*.jar ASM: a very small and fast Java bytecode manipulation framework Copyright (c) 2000-2005 INRIA, France Telecom All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: libs/notJars/fridaScripts/enumerate_classes.py ================================================ #!/usr/bin/python import frida import sys appName= sys.argv[1] print(appName) # put your javascript-code here jscode= """ Java.perform( function(){ var grepped = "ssl"; Java.enumerateLoadedClasses( { "onMatch": function(className){ //if (className.toLowerCase().includes(grepped)) console.log(className) }, "onComplete":function(){} } ) }) """ # startup frida and attach to com.android.chrome process on a usb device session = frida.get_usb_device().attach(appName) # create a script for frida of jsccode script = session.create_script(jscode) # and load the script script.load() session.detach() ================================================ FILE: libs/notJars/fridaScripts/get_attributes_output.js ================================================ setTimeout(function(){ Java.perform(function () { var className = Java.use("java.lang.String"); className.equals.implementation = function (arg0) { var returnValue = this.equals(arg0); console.log("Input: "+arg0); if (returnValue != null ) {console.log("Output: "+returnValue.toString());} return returnValue; }; }); },0); ================================================ FILE: libs/notJars/fridaScripts/unpin_sslContext.py ================================================ #!/usr/bin/python import frida import sys appName= sys.argv[1] def on_message(message, data): if message['type'] == 'send': print("[*] {0}".format(message['payload'])) else: print(message) js= """ //Originally written by Piergiovanni Cipolloni : https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida/ Java.perform(function (){ console.log(""); console.log("[.] Cert Pinning Bypass/Re-Pinning"); var CertificateFactory = Java.use("java.security.cert.CertificateFactory"); var FileInputStream = Java.use("java.io.FileInputStream"); var BufferedInputStream = Java.use("java.io.BufferedInputStream"); var X509Certificate = Java.use("java.security.cert.X509Certificate"); var KeyStore = Java.use("java.security.KeyStore"); var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory"); var SSLContext = Java.use("javax.net.ssl.SSLContext"); // Load CAs from an InputStream console.log("[+] Loading our CA...") cf = CertificateFactory.getInstance("X.509"); try { var fileInputStream = FileInputStream.$new("/data/local/tmp/not3naf.cer"); } catch(err) { console.log("[o] " + err); } var bufferedInputStream = BufferedInputStream.$new(fileInputStream); var ca = cf.generateCertificate(bufferedInputStream); bufferedInputStream.close(); var certInfo = Java.cast(ca, X509Certificate); console.log("[o] Our CA Info: " + certInfo.getSubjectDN()); // Create a KeyStore containing our trusted CAs console.log("[+] Creating a KeyStore for our CA..."); var keyStoreType = KeyStore.getDefaultType(); var keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // Create a TrustManager that trusts the CAs in our KeyStore console.log("[+] Creating a TrustManager that trusts the CA in our KeyStore..."); var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); var tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); console.log("[+] Our TrustManager is ready..."); console.log("[+] Hijacking SSLContext methods now...") console.log("[-] Waiting for the app to invoke SSLContext.init()...") SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").implementation = function(a,b,c) { console.log("[o] App invoked javax.net.ssl.SSLContext.init..."); SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;", "[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom").call(this, a, tmf.getTrustManagers(), c); console.log("[+] SSLContext initialized with our custom TrustManager!"); } }); """ session = frida.get_usb_device().attach(appName) script = session.create_script(js) script.load() #session.detach() ================================================ FILE: src/main/java/.gradle/2.10/taskArtifacts/cache.properties ================================================ #Fri Mar 06 02:03:30 CET 2020 ================================================ FILE: src/main/java/actions/Comparer.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package actions; import java.io.File; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import base.CopyUtil; import base.FileUtil; import base.OtherUtil; import commandExec.Commando; import db.DatabaseTester; import initialization.TicklerConst; import initialization.TicklerVars; public class Comparer { private Commando commando; private FileUtil fileTrans; private CopyUtil copyz; private String dataDirOld, extDirOld, storageOld; public Comparer() { this.copyz = new CopyUtil(); this.commando = new Commando(); this.fileTrans = new FileUtil(); } /* public void diffOld(boolean detailed) { this.clearDataDirs(); this.copyz.copyDataDir(TicklerVars.appTickDir+"DataDirOld/"); System.out.println("\n\n>>>>>>>>>>>>>>>> Go crazy then press Enter to compare data directories....\n"); OtherUtil.pressAnyKeySilent(); //Copy both local and external storage copyz.copyDataDir(); // copyz.copyStorage(); String command = "diff -rq "+ TicklerVars.appTickDir+"DataDirOld/ "+TicklerVars.appTickDir+"DataDir/"; String output = this.commando.executeCommand(command); System.out.println(output.replace(TicklerVars.appTickDir, "[Tickler_App_Dir]/")); if (output.isEmpty()) System.out.println("No change in the app's Data directory"); else System.out.println("\n...Where [Tickler_App_Dir] is "+TicklerVars.appTickDir); if (detailed){ this.diffDetailed(output); } } */ public void diff(boolean detailed) { //Init here this.storageOld = TicklerVars.transferDir+TicklerConst.DIFF_OLD_STORAGE; this.dataDirOld = this.storageOld+TicklerConst.DATA_DIR_NAME; this.extDirOld = this.storageOld+TicklerConst.EXTERNAL_STORAGE_Dir; String old_storage=TicklerConst.DIFF_OLD_STORAGE.substring(0, TicklerConst.DIFF_OLD_STORAGE.length()-1); this.clearDataDirs(); // this.copyz.copyStorage(TicklerConst.DIFF_OLD_STORAGE); this.copyz.copyStorage(old_storage); System.out.println("\n\n>>>>>>>>>>>>>>>> Go crazy then press Enter to compare data directories....\n"); OtherUtil.pressAnyKeySilent(); copyz.copyStorage(); String command = "diff -rq "+ this.dataDirOld +" "+TicklerVars.dataDir; String output = this.commando.executeCommand(command); System.out.println(output.replace(TicklerVars.appTickDir, "[Tickler_App_Dir]/")); // Quick and Dirty: Repeated for external storage command = "diff -rq "+ this.extDirOld +" "+TicklerVars.extDataDir; output = this.commando.executeCommand(command); System.out.println(output.replace(TicklerVars.appTickDir, "[Tickler_App_Dir]/")); if (output.isEmpty()) System.out.println("No change in the app's Data directory"); else System.out.println("\n...Where [Tickler_App_Dir] is "+TicklerVars.appTickDir); if (detailed){ this.diffDetailed(output); } } /** * A detailed output of changed files: * 1) text files: classic diff output * 2) DB files: dump and show the difference * @param output: output of undetailed diff */ public void diffDetailed(String output){ System.out.println("\nDetailed Comparison result:\n===========================\n"); //1- get file names ArrayList diffFileNames = this.getDiffFileNames(output); if (!diffFileNames.isEmpty()){ //2- diff files for (String s:diffFileNames) checkFileTypeAndCompare(s); } } private ArrayList getDiffFileNames(String output){ ArrayList filePathsArray = new ArrayList(); String[] opArray=output.split("\n"); if (opArray.length>0 && opArray[0]!=""){ for (String s:opArray) { Matcher m = Pattern.compile("differ$").matcher(s); if (m.find()){ String filePath = s.split(" ")[1]; filePathsArray.add(filePath); } } } return filePathsArray; } private void checkFileTypeAndCompare(String fileName){ File f = new File(fileName); String output,fileTypeCommand; fileTypeCommand = this.fileTrans.fileType(f); DatabaseTester dbT = new DatabaseTester(); ///File is a text file or human readable if (fileTypeCommand.contains("text")){ //File is a text file this.compareTextfiles(fileName); } //If the file is a database file else if (dbT.isFileDB(f)){ this.compareDB(f); } } private void compareTextfiles(String fileName){ String command,output,fName; fName=this.fileTrans.getFileNameFromPath(fileName); System.out.println("---------- "+fName+" ----------"); command="diff "+fileName+" "+fileName.replace("DataDirOld", "DataDir"); output=this.commando.executeCommand(command); System.out.println(output+"\n"); } /** * Comapres between 2 DBs by string diff their dumps * @param f */ private void compareDB(File f){ DatabaseTester dbT = new DatabaseTester(); String filePath=f.getAbsolutePath(); String oldDump = dbT.dumpDBToFile(filePath,null); String newDump = dbT.dumpDBToFile(filePath.replace("DataDirOld", "DataDir"),null); System.out.println("------- Database File: "+f.getName()+" -------"); String command = "diff "+ oldDump+" "+newDump; String output = this.commando.executeCommand(command); System.out.println(output); this.fileTrans.deleteFromHost(oldDump); this.fileTrans.deleteFromHost(newDump); } /** * Clears Datadir, ExtDataDir and their old versions before diff */ private void clearDataDirs(){ // this.fileTrans.warnOverrideAndDelete(TicklerVars.appTickDir+"DataDirOld/"); // this.fileTrans.warnOverrideAndDelete(TicklerVars.dataDir); String[] directories = {TicklerVars.dataDir,TicklerVars.extDataDir,this.dataDirOld,this.extDirOld}; for (String dir: directories) { this.fileTrans.warnOverrideAndDelete(dir); } } } ================================================ FILE: src/main/java/actions/Comparer_Old.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package actions; import java.io.File; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import base.CopyUtil; import base.FileUtil; import base.OtherUtil; import commandExec.Commando; import db.DatabaseTester; import initialization.TicklerConst; import initialization.TicklerVars; public class Comparer_Old { private Commando commando; private FileUtil fileTrans; private CopyUtil copyz; private String dataDirOld, extDirOld, storageOld; public Comparer_Old() { this.copyz = new CopyUtil(); this.commando = new Commando(); this.fileTrans = new FileUtil(); this.dataDirOld = TicklerVars.appTickDir+TicklerConst.DATA_DIR_OLD; this.extDirOld = TicklerVars.appTickDir+TicklerConst.EXT_DIR_OLD; this.storageOld = TicklerVars.appTickDir+"Storage_old/"; } public void diffOld(boolean detailed) { this.clearDataDirs(); this.copyz.copyDataDir(TicklerVars.appTickDir+"DataDirOld/"); System.out.println("\n\n>>>>>>>>>>>>>>>> Go crazy then press Enter to compare data directories....\n"); OtherUtil.pressAnyKeySilent(); //Copy both local and external storage copyz.copyDataDir(); // copyz.copyStorage(); String command = "diff -rq "+ TicklerVars.appTickDir+"DataDirOld/ "+TicklerVars.appTickDir+"DataDir/"; String output = this.commando.executeCommand(command); System.out.println(output.replace(TicklerVars.appTickDir, "[Tickler_App_Dir]/")); if (output.isEmpty()) System.out.println("No change in the app's Data directory"); else System.out.println("\n...Where [Tickler_App_Dir] is "+TicklerVars.appTickDir); if (detailed){ this.diffDetailed(output); } } public void diff(boolean detailed) { this.clearDataDirs(); this.copyz.copyStorage(this.storageOld); System.out.println("\n\n>>>>>>>>>>>>>>>> Go crazy then press Enter to compare data directories....\n"); OtherUtil.pressAnyKeySilent(); copyz.copyStorage(); String command = "diff -rq "+ this.dataDirOld +" "+TicklerVars.dataDir; } /** * A detailed output of changed files: * 1) text files: classic diff output * 2) DB files: query and show the difference * @param output: output of undetailed diff */ public void diffDetailed(String output){ System.out.println("\nDetailed Comparison result:\n===========================\n"); //1- get file names ArrayList diffFileNames = this.getDiffFileNames(output); if (!diffFileNames.isEmpty()){ //2- diff files for (String s:diffFileNames) checkFileTypeAndCompare(s); } } private ArrayList getDiffFileNames(String output){ ArrayList filePathsArray = new ArrayList(); String[] opArray=output.split("\n"); if (opArray.length>0 && opArray[0]!=""){ for (String s:opArray) { Matcher m = Pattern.compile("differ$").matcher(s); if (m.find()){ String filePath = s.split(" ")[1]; filePathsArray.add(filePath); } } } return filePathsArray; } private void checkFileTypeAndCompare(String fileName){ File f = new File(fileName); String output,fileTypeCommand; fileTypeCommand = this.fileTrans.fileType(f); DatabaseTester dbT = new DatabaseTester(); ///File is a text file or human readable if (fileTypeCommand.contains("text")){ //File is a text file this.compareTextfiles(fileName); } //If the file is a database file else if (dbT.isFileDB(f)){ this.compareDB(f); } } private void compareTextfiles(String fileName){ String command,output,fName; fName=this.fileTrans.getFileNameFromPath(fileName); System.out.println("---------- "+fName+" ----------"); command="diff "+fileName+" "+fileName.replace("DataDirOld", "DataDir"); output=this.commando.executeCommand(command); System.out.println(output+"\n"); } /** * Comapres between 2 DBs by string diff their dumps * @param f */ private void compareDB(File f){ DatabaseTester dbT = new DatabaseTester(); String filePath=f.getAbsolutePath(); String oldDump = dbT.dumpDBToFile(filePath,null); String newDump = dbT.dumpDBToFile(filePath.replace("DataDirOld", "DataDir"),null); System.out.println("------- Database File: "+f.getName()+" -------"); String command = "diff "+ oldDump+" "+newDump; String output = this.commando.executeCommand(command); System.out.println(output); this.fileTrans.deleteFromHost(oldDump); this.fileTrans.deleteFromHost(newDump); } /** * Clears Datadir, ExtDataDir and their old versions before diff */ private void clearDataDirs(){ // this.fileTrans.warnOverrideAndDelete(TicklerVars.appTickDir+"DataDirOld/"); // this.fileTrans.warnOverrideAndDelete(TicklerVars.dataDir); String[] directories = {TicklerVars.dataDir,TicklerVars.extDataDir,this.dataDirOld,this.extDirOld}; for (String dir: directories) { this.fileTrans.warnOverrideAndDelete(dir); } } } ================================================ FILE: src/main/java/actions/Searcher.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package actions; import java.util.ArrayList; import java.io.File; import java.util.AbstractMap.SimpleEntry; import base.Base64Util; import base.CopyUtil; import base.FileUtil; import base.OtherUtil; import base.SearchUtil; import cliGui.OutBut; import db.DatabaseTester; import info.InfoGathering; import initialization.TicklerConst; import initialization.TicklerVars; public class Searcher { private SearchUtil searchUtil; private String codeLoc; private CopyUtil copyUtil; private FileUtil fileUtil; public Searcher() { this.searchUtil = new SearchUtil(); this.codeLoc = TicklerVars.jClassDir; this.copyUtil = new CopyUtil(); this.fileUtil = new FileUtil(); } /** * Searches for a key in decompiled java code and in strings.xml * @param key */ public void sC(String key,boolean all){ ArrayList hits = this.searchInCodeWithOption(key, all); OtherUtil.printSimpleEntryArray(hits, this.codeLoc, "[Java_Code_Dir]"); //Search in Manifest ArrayList result = new ArrayList<>(); result = this.searchUtil.search4KeyInDirFName(TicklerVars.tickManifestFile, key); if (!result.isEmpty()) { OutBut.printH2("Search results in Manifest File"); OtherUtil.printSimpleEntryArray(result, TicklerVars.tickManifestFile, "[res]"); } // Search for a key in res directory and print the results this.searchInRes(key); } private ArrayList sCInFile(File file, String fileName, String key){ ArrayList result = new ArrayList<>(); if (file.exists()){ OutBut.printH2("Searching for "+key+" in "+fileName); result = this.searchUtil.findInFile(file, key); } return result; } /** * Search for a key in the res directory and print the results * @param key * @return */ private void searchInRes(String key){ ArrayList result = new ArrayList<>(); String resDir= TicklerVars.extractedDir+"res/"; result = this.searchUtil.search4KeyInDirFName(resDir, key); if (!result.isEmpty()) { OutBut.printH2("Search results in res directory"); OtherUtil.printSimpleEntryArray(result, resDir, "[res]"); } } /** * Search fo rkey in strings and arrays.xml but I don't all it as it is replaced by searchinRes() * @param key */ private void searchInStringsArrays(String key) { File stringsXml = new File(TicklerVars.extractedDir+"res/values/strings.xml"); File arraysXml = new File(TicklerVars.extractedDir+"res/values/arrays.xml"); ArrayList hits2 = this.sCInFile(stringsXml, "strings.xml", key); hits2.addAll(this.sCInFile(arraysXml, "arrays.xml", key)); if (!hits2.isEmpty()) for (String s: hits2) OutBut.printNormal(" "+s+"\n"); } /** * Searches in source code in a custom location. * @param key * @param loc */ public void scCustomCodeLoc(String key, String codeRoot){ if (codeRoot != null){ String codeRootNotHome=codeRoot.replace("~", System.getProperty("user.home")); File cR = new File(codeRootNotHome); if (cR.exists()){ this.codeLoc = codeRootNotHome; } else { OutBut.printError("The code location you entered "+codeRoot+" does not exist"); OutBut.printStep("Using decompiled Java code from the APK...."); } } this.sC(key,false); } /** * Searches in code based on option: sc_all --> all = true, sc --> all=false * @param key * @param all * @return */ private ArrayList searchInCodeWithOption(String key, boolean all){ ArrayList hits = new ArrayList<>(); if (all){ OutBut.printH2("Searching for "+key+" in Decompiled Java Code"); hits = this.searchUtil.search4KeyInDirFName(this.codeLoc, key) ; } else { OutBut.printH2("Searching for "+key+" in Decompiled Code of the app"); hits = this.searchUtil.searchForKeyInJava(key, this.codeLoc); } return hits; } /** * Search for a key in the Data directory of an App, including files and unencrypted databases. * Also search for the key in clear text and in Base64 format * Also check in External Dir if exists * @param key */ public void searchForKeyInDataDir(String key, boolean isCopy){ if (isCopy){ OutBut.printStep("Updating Data Directory"); CopyUtil copyz = new CopyUtil(); copyz.copyDataDir(); } else { OutBut.printWarning("Data storage directory is not updated"); } //Search in files OutBut.printH2("Searching Files in Data Directory of the app"); ArrayList hits = this.searchUtil.search4KeyInDirFName(TicklerVars.dataDir, key); OtherUtil.printSimpleEntryArray(hits, TicklerVars.dataDir, "[Data_Dir]"); // Search Base64 this.base64Search(key); //Search in DB OutBut.printH2("Searching Files in the app's unencrypted databases"); DatabaseTester db = new DatabaseTester(); db.searchForKeyInDb(key); //search in external memory this.searchExternalStorage(key); } private void base64Search(String key){ Base64Util b64 = new Base64Util(); ArrayList base64Hits = b64.searchB64DataDir(key); if (!base64Hits.isEmpty()){ OutBut.printH2("The key is base64 encrypted in the following file(s)"); for (String s: base64Hits){ String filePath = s.replaceAll(this.codeLoc, "[Data_Dir]"); System.out.println("#FileName: "+filePath); } } } /** * Copy external storage into transfers file * Then saerch in it * Copying code moved to CopyUtil and a new method is created only to search there * @param key */ /* private void searchExternalStorage(String key) { InfoGathering info = new InfoGathering(); FileUtil fU = new FileUtil(); String extDir = info.getSdcardDirectory().replaceAll("\\n", ""); String destExtDir=TicklerVars.transferDir+TicklerConst.COPIED_EXTERNAL_STORAGE_NAME; if (!extDir.isEmpty()){ OutBut.printH2("Searching the app's external memory"); fU.copyDirToHost(extDir, destExtDir,true); System.out.println(""); OutBut.printStep("Copying External Storage Directory: "+extDir+"\n"); ArrayList hits = this.searchUtil.search4KeyInDirFName(destExtDir, key); OtherUtil.printSimpleEntryArray(hits, extDir, "[External_Dir]"); } }*/ /** * Searching for a key in External Directory * @param key */ private void searchExternalStorage(String key) { //OutBut.printStep("Updating External Storage Directory\n"); if ( fileUtil.isExistOnDevice(TicklerVars.extDataDir)) { copyUtil.copyExtDir(TicklerVars.extDataDir); ArrayList hits = this.searchUtil.search4KeyInDirFName(TicklerVars.extDataDir, key); OtherUtil.printSimpleEntryArray(hits, TicklerVars.extDataDir, "[External_Dir]"); } } } ================================================ FILE: src/main/java/actions/Snapshots.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package actions; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import base.FileUtil; import cliGui.OutBut; import commandExec.Commando; import initialization.TicklerVars; public class Snapshots { private Commando commando; private FileUtil fileTrans; public Snapshots() { this.commando = new Commando(); this.fileTrans = new FileUtil(); } public void takeSnapshot(){ String timestamp = new SimpleDateFormat("dd.MM.yy_HH.mm.ss").format(new Date()); String imgName = TicklerVars.pkgName+"_"+timestamp+".png"; String path=TicklerVars.sdCardPath+imgName; //In case of no package this.fileTrans.createDirOnDevice(TicklerVars.sdCardPath); this.executeSnapshot(path); String imgDir=TicklerVars.imageDir; this.fileTrans.createDirOnHost(imgDir); this.fileTrans.pullFromSDcard(path, imgDir); if (new File(TicklerVars.imageDir+imgName).exists()) OutBut.printStep("Screenshot taken succesfully and saved at "+TicklerVars.imageDir+imgName); } public void executeSnapshot(String path) { String command = "screencap -p "+path; this.commando.execADB(command); } public void getBackGroundSnapshots(){ //quick and dirty: try both locations // String path = "/data/system/recent_images"; String[] paths = {"/data/system_ce/0/recent_images","/data/system/recent_images", "/data/system_ce/0/snapshots/" }; this.fileTrans.createDirOnHost(TicklerVars.bgSnapshotsDir); OutBut.printH1("Collecting stored Background screenshots"); OutBut.printH2("Screenshots are stored in only one of the following location. So ignore \"Directory does not exist\" errors if 1 location does not report any error"); System.out.println(); System.out.println("Copying Background snapshots to host..."); for (String path : paths) this.fileTrans.copyDirToHost(path, TicklerVars.bgSnapshotsDir,false); } } ================================================ FILE: src/main/java/apk/ApkSigner.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package apk; import java.io.File; import cliGui.OutBut; import commandExec.Commando; import initialization.TicklerVars; /** * Signs an APK if Jarsigner exists on the device * @author aabolhadid * */ public class ApkSigner { private boolean checkJarsigner(){ //check if jarsigner exists Commando commando = new Commando(); String cmd = "jarsigner -h"; int result = commando.executeProcessListPrintOP(cmd, false); if (result == 0) return true; return false; } private boolean isKeyStoreExist(){ File keystore = new File(TicklerVars.keyStore); return keystore.exists(); } public boolean signApk(String apkPath){ boolean ok = false; if (!this.checkJarsigner()){ OutBut.printWarning("Cannot sign the new apk because jarsigner is not found on the host"); OutBut.printNormal("\nThe debuggable apk needs to be signed, in order to be installed on the device.\njarsigner can be installed by installing Java JDK\n"); } //jarsigner exists else if (!this.isKeyStoreExist()){ OutBut.printError("Keystore needed to sign the new apk is not found at "+TicklerVars.keyStore); } else{ OutBut.printStep("Signing the app using Tickler keystore"); String cmd = "jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore "+TicklerVars.keyStore+" -storepass itsalright "+apkPath+" Tickler"; Commando commando = new Commando(); int result = commando.executeProcessListPrintOP(cmd, true); if (result == 0) ok = true; } return ok; } } ================================================ FILE: src/main/java/apk/ApkToolClass.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package apk; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import org.apache.commons.io.FileUtils; import base.FileUtil; import brut.androlib.Androlib; import brut.androlib.ApkDecoder; import brut.androlib.ApkOptions; import cliGui.OutBut; import commandExec.Commando; import exceptions.TNotFoundEx; import initialization.TicklerVars; /** * Responsible for APKtool actions * @author aabolhadid * */ public class ApkToolClass { String debuggableApk; public ApkToolClass(){ this.debuggableApk = TicklerVars.appTickDir+"debuggable.apk"; } /** * APK tool decompiles the apk into Manifest, res dir and Smali files, * @throws TNotFoundEx if apktool fails to get the manifest file * @param apkPath */ public void apkToolDecode(String apkPath) throws TNotFoundEx{ FileUtil fileT = new FileUtil(); brut.androlib.ApkDecoder dec = new ApkDecoder(); File apkPathFile = new File(apkPath); dec.setApkFile(apkPathFile); this.deleteExistingDecodedDir(); try { File file = new File("/dev/null"); PrintStream ps = new PrintStream(new FileOutputStream(file)); System.setErr(ps); dec.setOutDir(new File (TicklerVars.extractedDir)); dec.decode(); System.setErr(System.err); } catch(Exception e){ e.printStackTrace(); } try { fileT.copyOnHost(TicklerVars.extractedDir+"AndroidManifest.xml", TicklerVars.tickManifestFile,true); } catch(Exception e){ OutBut.printError("Decompilation failed and Manifest cannot be obtained"); TNotFoundEx eNotFound = new TNotFoundEx("Decompilation failed and Manifest cannot be obtained"); throw eNotFound; } } /** * Compile a customized app to an apk * @param dirPath the directory of the modififed app * @param apkPath output apk */ public void apkToolCompile(String dirPath, String apkPath){ String tempApktoolLog = TicklerVars.logDir+"debuggableCompile.log"; ApkOptions apkOptions = new ApkOptions(); try{ File file = new File(tempApktoolLog); OutBut.printH3("APKTool output:"); new Androlib(apkOptions).build(new File(dirPath), new File(apkPath)); } catch(Exception e){ e.printStackTrace(); } } private void deleteExistingDecodedDir(){ File decodedDir = new File (TicklerVars.extractedDir); if (decodedDir.exists()){ try{ FileUtils.deleteDirectory(decodedDir); System.out.println("Existing SMALI directory will be deleted...."); } catch(IOException e){ System.out.println("Cannot delete old extracted directory "+decodedDir.getAbsolutePath()+"\nPlease delete it manually and repeat");; } } } } ================================================ FILE: src/main/java/apk/ApkToolDude.java ================================================ package apk; import java.io.File; import java.io.FileOutputStream; import java.io.PrintStream; import base.FileUtil; import brut.androlib.Androlib; import brut.androlib.ApkOptions; import cliGui.OutBut; import commandExec.Commando; import exceptions.TNotFoundEx; import initialization.TicklerVars; // Replacing ApkToolClass in this version to run apktool.jar ... rewritten in v3 public class ApkToolDude { String debuggableApk; public ApkToolDude(){ this.debuggableApk = TicklerVars.appTickDir+"debuggable.apk"; } /** * Normal apkdecode as pervious versions... decompiling resources * @param apkPath * @throws TNotFoundEx */ public void apkToolDecode(String apkPath) throws TNotFoundEx { String args = " d -o "+TicklerVars.extractedDir+" "+apkPath; this.apkToolDecodeGeneral(apkPath, args); } private void apkToolDecodeGeneral(String apkPath, String args) throws TNotFoundEx{ FileUtil fileT = new FileUtil(); File apkPathFile = new File(apkPath); try { File file = new File("/dev/null"); PrintStream ps = new PrintStream(new FileOutputStream(file)); System.setErr(ps); this.executeApktoolCommand(args); System.setErr(System.err); } catch(Exception e){ e.printStackTrace(); } try { fileT.copyOnHost(TicklerVars.extractedDir+"AndroidManifest.xml", TicklerVars.tickManifestFile,true); } catch(Exception e){ OutBut.printError("Decompilation failed and Manifest cannot be obtained"); TNotFoundEx eNotFound = new TNotFoundEx("Decompilation failed and Manifest cannot be obtained"); throw eNotFound; } } public void apkToolCompile(String dirPath, String apkPath){ String tempApktoolLog = TicklerVars.logDir+"debuggableCompile.log"; String args = " b "+dirPath+" -o "+apkPath; // ApkOptions apkOptions = new ApkOptions(); try{ File file = new File(tempApktoolLog); OutBut.printH3("APKTool output:"); this.executeApktoolCommand(args); // new Androlib(apkOptions).build(new File(dirPath), new File(apkPath)); } catch(Exception e){ e.printStackTrace(); } } /** * Wrapper of apktool.jar * @param args */ private void executeApktoolCommand(String args) { Commando command = new Commando(); String cmd = "java -jar "+TicklerVars.apktoolPath + args; command.executeProcessString(cmd); } } ================================================ FILE: src/main/java/apk/AppBroker.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package apk; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.commons.io.FileUtils; import base.OtherUtil; import cliGui.OutBut; import commandExec.Commando; import components.Activity; import components.Manifest; import initialization.TicklerVars; /** * Starts, stops, (un)install apps, backs up data dir before installation of an enhanced app * @author aabolhadid * */ public class AppBroker { private String pkgName,outputPath,sdCardPath; private Commando commando; /** * @param laucher * @param pkgName */ public AppBroker(Activity laucher,String pkgName) { super(); this.pkgName = pkgName; this.commando = new Commando(); this.sdCardPath=TicklerVars.sdCardPath; } public AppBroker(String pkgName) { super(); this.pkgName = pkgName; this.commando = new Commando(); } public String forceStopApp() { return "am force-stop "+this.getPkgName(); } ///////////////////////// Install and Uninstall public void installApk(String apk){ String command = "adb install "+apk; this.commando.executeProcessListPrintOP(command, true); } public void uninstallPackage(String pkgName){ String command = "pm uninstall "+pkgName; this.commando.execRootPrintOP(command); } //////////////////////Backup Data Directory public void backupDataDir(String bkpLoc){ File destLoc; File appDir = new File(TicklerVars.appTickDir); if (appDir.exists()) { if (bkpLoc == null){ String timestamp = new SimpleDateFormat("dd-MM-yy_HH.mm.ss").format(new Date()); destLoc=new File(TicklerVars.ticklerDir+TicklerVars.pkgName+"_bkp_"+timestamp); } else destLoc = new File(bkpLoc); try{ // FileUtils.moveDirectory(appDir, destLoc); Files.move(appDir.toPath(), destLoc.toPath(), StandardCopyOption.REPLACE_EXISTING); // FileUtils.moveDirectoryToDirectory(destLoc, appDir, true); } catch(IOException e){ e.printStackTrace(); } } } private boolean isReinstallQuestion(){ OutBut.printWarning("\nDo you want to uninstall the original app and install the modified one?"); OutBut.printNormal("If yes, the following steps are executed:"); OutBut.printNormal("\t1- Uninstall the original app"); OutBut.printNormal("\t2- Backup Tickler working directory, if it exists "); OutBut.printNormal("\t3- Install the modified app"); OutBut.printNormal("\nIf agree, enter yes or y"); String choice = OtherUtil.pressAnyKeySilent(); if (choice.toLowerCase().equals("yes") || choice.toLowerCase().equals("y")) return true; return false; } /** * After creation of debuggable or MitM, the user can uninstall the original APK and install the modified one * @param apk */ public void reinstall(String apk){ if (this.isReinstallQuestion()){ this.uninstallPackage(TicklerVars.pkgName); this.installApk(apk); this.backupDataDir(null); } } ////////////// Getters and Setters public String getPkgName() { return pkgName; } public void setPkgName(String pkgName) { this.pkgName = pkgName; } public String getOutputPath() { return outputPath; } public void setOutputPath(String outputPath) { this.outputPath = outputPath; } public String getSdCardPath() { return sdCardPath; } } ================================================ FILE: src/main/java/apk/Decompiler.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package apk; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.List; import base.FileUtil; import base.SearchUtil; import cliGui.OutBut; import commandExec.Commando; import initialization.TicklerVars; public class Decompiler { private String jarLoc; private boolean checkD2jExists() { if (new File(TicklerVars.dex2jarPath).exists()){ return true; } System.out.println("!!!!!ERROR: The stand alone version does not support dex2jar"); return false; } private File getApkFromTicklerDir(){ File apkFile; SearchUtil searcher = new SearchUtil(); String[] keys ={"base.apk"}; List apkList = searcher.search4FileInDir(TicklerVars.appTickDir, keys); apkFile = apkList.get(0); if (apkFile.getName().equals("debuggable.apk")) apkFile = apkList.get(1); return apkFile; } public void decompile(){ this.dex2jar(); this.jdCore(); } public void dex2jar() { //If dex2jar scripts exist and executable if (this.checkD2jExists() && this.checkDex2JarExecutable()){ FileUtil ft = new FileUtil(); Commando command = new Commando(); ft.createDirOnHost(TicklerVars.dex2jarDir); this.jarLoc = TicklerVars.dex2jarDir+"app.jar"; String cmd = TicklerVars.dex2jarPath + " "+ this.getApkFromTicklerDir() +" -o "+TicklerVars.jClassDir+".jar"; OutBut.printStep("Decompiling the app using Dex2Jar tool......"); command.executeProcessString(cmd); } } private void jdCore(){ try{ OutBut.printStep("Obtaining Java code using JDCore tool. This might take some time ......"); // String cmd = "java -jar /home/a7mad/tools/Android/Decompile/JD-core/jd-core-1.1.3.jar "+TicklerVars.jClassDir+".jar "+TicklerVars.jClassDir; // Commando command = new Commando(); // command.executeProcessListPrintOP(cmd,true); // // new jd.core.Decompiler().decompile(TicklerVars.jClassDir+".zip", TicklerVars.jClassDir); new jd.core.Decompiler().decompile(TicklerVars.jClassDir+".jar", TicklerVars.jClassDir); } catch(Exception e){ //e.printStackTrace(); } } /** * If required dex2jar files are executable * @param path */ private boolean checkDex2JarExecutable(){ boolean isExec = true; FileUtil ft = new FileUtil(); Commando command = new Commando(); String d2jInvokeFile = TicklerVars.dex2jarPath.replace("d2j-dex2jar.sh", "d2j_invoke.sh"); String[] execs = {TicklerVars.dex2jarPath, d2jInvokeFile}; for (String exec : execs){ if (!ft.isExecutable(exec)){ isExec = false; OutBut.printError("Please change mode of "+exec+" to executable and rerun the command" ); } } return isExec; } /** * return if dex2jar exists and executable * @return */ public boolean isDex2Jar() { return this.checkD2jExists() && this.checkDex2JarExecutable(); } } ================================================ FILE: src/main/java/apk/newApks/CreateApk.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package apk.newApks; import java.io.File; import apk.ApkSigner; import apk.ApkToolClass; import apk.ApkToolDude; import apk.AppBroker; import base.FileUtil; import cliGui.OutBut; import exceptions.TNotFoundEx; import initialization.TicklerVars; import initialization.TicklerConst; /** * Creates new APK versions of the original APK * MITM: a modification to network secrutiy configuration of the app in order to accept user added certificates to trust store * @author aabolhadid * */ public class CreateApk { private ApkToolDude apktool; private FileUtil ft; private ApkSigner signer; private String newAppDir,netSecConfFileName,netSecConfFilePath,apk; private boolean isMitm; private INewApk newApk; public CreateApk(int apkID){ // Ahmad: replacing ApkToolClass this.apktool = new ApkToolDude(); this.ft = new FileUtil(); this.signer = new ApkSigner(); this.newAppDir = TicklerVars.appTickDir+TicklerConst.newAppTempDir; this.netSecConfFileName = TicklerConst.mitmXmlName; this.netSecConfFilePath = newAppDir+"res/xml/"+netSecConfFileName; this.initNewApp(apkID); } private void initNewApp(int apkID){ switch(apkID){ case TicklerConst.debuggable: newApk = new Debuggable(); break; case TicklerConst.mitm: newApk = new NougatMitM(); break; } } /** * Create a new APK, debuggable or mitm compatible * @param isInstall whether to install the apk after creation */ public void createNewApk(){ boolean successfulSign; try { OutBut.printWarning("Decompiling and Recompiling of the APK might have some errors, which might lead to incorrect behavior of the modified app"); this.apk = this.newApk.getNewApkName(); this.ft.copyOnHost(TicklerVars.extractedDir, TicklerVars.newApkTempDir, true); this.newApk.changeManifest(); this.apktool.apkToolCompile(this.newAppDir,this.apk); this.afterCompilation(false); successfulSign =this.signer.signApk(this.apk); if (successfulSign) this.reinstallNewApk(); } catch(TNotFoundEx e){ OutBut.printError(e.getMessage()); } catch(Exception e) { e.printStackTrace(); } } public void createAnyApk(String decompiledDir, String name) { this.apk = TicklerVars.appTickDir+name; OutBut.printStep("Creating a new APK at: "+this.apk); this.apktool.apkToolCompile(decompiledDir,this.apk); this.afterCompilation(true); boolean successfulSign =this.signer.signApk(this.apk); if (successfulSign) this.reinstallNewApk(); } /** * * @param isCustom boolean if it's creating a custom APK (not dbg nor mitm) then don't delete the source directory */ private void afterCompilation(boolean isCustom) { if (!isCustom) this.ft.deleteFromHost(TicklerVars.newApkTempDir); if ( this.ft.isExist(this.apk)){ System.out.println("\n\n"); OutBut.printStep("App is created successfully at "+this.apk+" \n"); } } private void reinstallNewApk(){ AppBroker broker = new AppBroker(TicklerVars.pkgName); broker.reinstall(this.apk); } } ================================================ FILE: src/main/java/apk/newApks/Debuggable.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package apk.newApks; import java.io.IOException; import base.FileUtil; import initialization.TicklerConst; import initialization.TicklerVars; public class Debuggable implements INewApk { private String newAppDir; private FileUtil fU; public Debuggable(){ this.newAppDir = TicklerVars.appTickDir+TicklerConst.newAppTempDir; this.fU = new FileUtil(); } @Override public String getNewApkName() { return TicklerVars.appTickDir+TicklerConst.debuggableName; } @Override public void changeManifest() { try { String manString = this.fU.readFile(newAppDir+"AndroidManifest.xml"); //DEbuggable if (manString.contains("android:debuggable")) manString = manString.replaceAll("debuggable=\"false\"", "debuggable=\"true\""); else manString = manString.replaceAll("\n" +"\n" +"\n" +" \n" +"\n" +" \n" +"\n" +"\n" +""; this.fU.writeFile(netSecConfFilePath, xmlFile); } } ================================================ FILE: src/main/java/attacks/ActivityStarter.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package attacks; import java.util.ArrayList; import commandExec.Commando; import components.Activity; import components.IActivityService; import components.Intent; import manifest.handlers.IntentHandler; /** * * @author aabolhadid * Starts Activities */ public class ActivityStarter { String pkgName; String actStartCommand; IActivityService activity; ArrayList commands; Commando c; public ActivityStarter(String pkgname) { this.pkgName = pkgname; this.commands = new ArrayList(); } public ArrayList startActivityfully(IActivityService actSer) { ArrayList commands=new ArrayList(); String startCommand = this.startActivity(actSer); commands.add(startCommand); IntentHandler iHandler; if (actSer.getIntent()!= null) { for (Intent i:actSer.getIntent()) { iHandler= new IntentHandler(startCommand,i); commands.addAll(iHandler.fullIntent()); } } return commands; } /** * Starts activity without intents * @return Command to start */ public String startActivity() { String amCommand = this.createAmCommand(this.activity); return amCommand+this.getPkgName()+"/" + this.getActivity().getName(); } public String startActivity(IActivityService actSer){ this.setActivity(actSer); return this.startActivity(); } private String createAmCommand(IActivityService comp) { if (comp.getClass().equals(Activity.class)){ return "am start -n "; } return "am startservice -n "; } public String getPkgName() { return this.pkgName; } public void setPkgName(String pkgName) { this.pkgName = pkgName; } public IActivityService getActivity() { return activity; } public void setActivity(IActivityService a) { this.activity = a; } public Commando getC() { return c; } public void setC(Commando c) { this.c = c; } } ================================================ FILE: src/main/java/attacks/Broadcaster.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package attacks; import java.util.ArrayList; import components.Intent; import components.Receiver; import manifest.handlers.IntentHandler; public class Broadcaster { String pkgName; Receiver rec; IntentHandler iHandler; public Broadcaster(String pkgName) { this.pkgName = pkgName; } public Broadcaster(String pkgName,Receiver rec) { this.pkgName = pkgName; this.setRec(rec); } public Receiver getRec() { return rec; } public void setRec(Receiver rec) { this.rec = rec; } public ArrayList generateBroadcast(Receiver rec) { ArrayList commands = new ArrayList(); String baseCommand = "am broadcast -n "+this.pkgName+"/"+rec.getName(); commands.add(baseCommand); if (rec.getIntent() != null) for (Intent i : rec.getIntent()){ iHandler = new IntentHandler(baseCommand,i); commands.addAll(iHandler.fullIntent()); } return commands; } } ================================================ FILE: src/main/java/attacks/ProviderAttacker.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package attacks; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.AbstractMap.SimpleEntry; import java.util.regex.Matcher; import java.util.regex.Pattern; import base.SearchUtil; import components.Provider; /** * 1- Get content URIs from provider authorities * 2- Get content URIs from DEX * 3- Add content URIs from 2 if do not exist * @author aabolhadid * */ public class ProviderAttacker { private ArrayList contentURIs; private Provider provider; /////////////////// SMALI ////////////////////// public ArrayList queryUrisFromSmali(String smaliPath){ ArrayList commands = new ArrayList(); this.getContentURIsFromSmali(smaliPath); for (String uri:this.contentURIs) commands.add(this.queryContent(uri)); return commands; } public void getContentURIsFromSmali(String smaliPath){ this.contentURIs = new ArrayList(); ArrayList contents = new ArrayList(); ArrayList contentsReturn =new ArrayList(); SearchUtil searcher = new SearchUtil(); String contentUri; contents = searcher.search4KeyInDir(smaliPath, "content://"); for (String c:contents){ if (!"".equals(contentUri = this.correctContentUri(c))) this.contentURIs.add(contentUri); } this.contentURIs = this.removeDuplicates(this.contentURIs); } private String correctContentUri(String line) { String contentUri=""; //Content URI = content://+any_number_of(non space char) until the first occurrence of " or ' or a space character (space or new line) Matcher m = Pattern.compile("content://(\\S+?)[\"'\\s]").matcher(line); if (m.find()) { contentUri = line.substring(m.start(0),m.end(0)-1); } return contentUri; } private void checkAndAddUri(String uri) { if (!uri.equals("content://")){ this.contentURIs.add(uri); } if (!uri.endsWith("/")){ this.contentURIs.add(uri+"/"); } } ///////////////////////////// From manifest /////////////////////////// public String prepareContentFromAuthority(Provider prov){ String uri= "content://"+prov.getAuthorities(); return uri; } ////////////////////////// Query ///////////////////////////// public String queryContent(String contentURI) { return "content query --uri "+contentURI; } public ArrayList queryContents(){ ArrayList queryCommands = new ArrayList(); for (String cont:this.contentURIs){ queryCommands.add(this.queryContent(cont)); } return queryCommands; } /** * 1- get content URI from authorities * 2- add related Content URIs * 3- query content * 4- sql injection * @param prov * @return */ public ArrayList attackProvider(Provider prov){ ArrayList relatedUris = new ArrayList(); ArrayList commands = new ArrayList(); String uriFromAuth = this.prepareContentFromAuthority(prov); relatedUris.add(uriFromAuth); commands.add(this.queryContent(uriFromAuth)); for (String uri:this.contentURIs){ if (uri.contains(prov.getAuthorities()) && !uri.equals(uriFromAuth)){ relatedUris.add(uri); commands.add(this.queryContent(uri)); } } return commands; } public ArrayList getContentURIs() { return contentURIs; } public void setContentURIs(ArrayList contentURIs) { this.contentURIs = contentURIs; } private ArrayList removeDuplicates(ArrayList orig){ return new ArrayList(new LinkedHashSet(orig)); } } ================================================ FILE: src/main/java/attacks/StartAttack.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package attacks; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.LinkedHashSet; import actions.Snapshots; import apk.AppBroker; import base.OtherUtil; import cliGui.OutBut; import commandExec.Commando; import initialization.TicklerVars; import logs.LogReader; import logs.LogReaderController; /** * Migrating the start components functionality from Tickler class without missing up with Starter class * @author aabolhadid * */ public class StartAttack extends Starter { private AppBroker broker; private Commando commando; private ArrayList commands; private boolean isLogger; private Runnable log; private Thread th1; private String logFileName; private LogReaderController logController; private Snapshots snaps; public StartAttack(){ super(); this.broker = new AppBroker(TicklerVars.pkgName); this.commando = new Commando(); this.snaps = new Snapshots(); } /** * Replaces executeAttackCommand. * @param origCommands * @param exported * @param output */ public void executeTriggerCommands(ArrayList origCommands, boolean exported){ String anyKey=""; ArrayList commands = this.removeDuplicates(origCommands); this.printCommandsToBeExecuted(commands); //Executing commands for (String command : commands) { OutBut.printH2("Attacking Component"); System.out.println(command); if (this.isLogger()){ this.writeCommandInLogFile(command); } // Quick and dirty: don't start the app before bcast receiver and content provider attacks if (command.contains("content") || command.contains("am broadcast")){ } if (!exported) this.commando.execRootPrintOP(command); else this.commando.execADBPrintOP(command); anyKey=OtherUtil.pressAnykey(); if (anyKey.equals("snapshot")){ this.snaps.takeSnapshot(); } // Do not terminate app between Tickles //this.commando.execADB(this.broker.forceStopApp()); } } /** * Print in the beginning the list of commands to be executed */ private void printCommandsToBeExecuted(ArrayList origCommands){ if (this.isLogger){ OutBut.printH2("Logcat messages are saved in the following file: "); OutBut.printNormal(this.logFileName); } OutBut.printH2("Commands to be executed"); ArrayList commands = this.removeDuplicates(origCommands); for (String c:commands) System.out.println(c); } private ArrayList removeDuplicates(ArrayList orig){ return new ArrayList(new LinkedHashSet(orig)); } public ArrayList getCommands() { return commands; } public void setCommands(ArrayList commands) { this.commands = commands; } public boolean isLogger() { return isLogger; } public void setLogger(boolean isLogger) { this.isLogger = isLogger; if (isLogger){ this.prepareLoggerThread(); } } ////////////////////// Logging //////////////////////////////////// /** * Set and start the logger thread */ private void prepareLoggerThread(){ this.makeLogFileName(); this.logController = new LogReaderController(); this.logController.setLogFileName(this.logFileName); this.logController.setStop(false); this.log = new LogReader(this.logController); this.th1 = new Thread(this.log); th1.start(); } private void makeLogFileName(){ String timestamp = new SimpleDateFormat("dd.MM.yy_HH.mm.ss").format(new Date()); this.logFileName=TicklerVars.logDir+TicklerVars.pkgName+"_"+timestamp+".log"; } private synchronized void writeCommandInLogFile(String command){ File logFile = new File(this.logFileName); String line="\n\n************************************ Tickler: Executing Command ************************************************\n" +command+"\n *******************************************************************************************************************\n\n"; try{ FileWriter w = new FileWriter(logFile,true); w.append(line); w.close(); } catch (IOException e){ e.printStackTrace(); } } public void stopLogging(){ if (this.logController != null) this.logController.setStop(true); } } ================================================ FILE: src/main/java/attacks/Starter.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package attacks; import java.util.ArrayList; import cliGui.OutBut; import code.ExtrasUtil; import components.IActivityService; import components.IComponent; import components.Provider; import components.Receiver; import initialization.TicklerVars; /** * I mean component attacker :) so it triggers attacks of Activities, services, content providers and B/C receivers * @author aabolhadid * */ public class Starter { private ActivityStarter actStarter; private Broadcaster broadcaster; public ProviderAttacker provAtt; public String manifestPath; public Starter() { String pkgName = TicklerVars.pkgName; this.actStarter = new ActivityStarter(pkgName); this.broadcaster = new Broadcaster(pkgName); this.provAtt = new ProviderAttacker(); } /** * Collects commands of starting the selected components * @param components * @return */ public ArrayList attackComponents(ArrayList components){ ArrayList commands = new ArrayList(); for (IComponent c:components){ commands.addAll(this.attackComponent(c)); } return commands; } /** * Collect the commands to start a specific component, based on its type * @param comp * @return */ public ArrayList attackComponent(IComponent comp) { ArrayList commands = new ArrayList(); if (comp instanceof IActivityService) { commands.addAll(this.actStarter.startActivityfully((IActivityService)comp)); } else if (comp instanceof Receiver) { commands.addAll(broadcaster.generateBroadcast((Receiver) comp)); } else if (comp instanceof Provider) { //Query only the URI from Authority String uri = this.provAtt.prepareContentFromAuthority((Provider)comp); commands.add(this.provAtt.queryContent(uri)); } ExtrasUtil cU = new ExtrasUtil(); if (cU.isJClassDir()){ ArrayList commandsWithExtras = this.addExtrasOfComp(comp, commands); return commandsWithExtras; } else { OutBut.printWarning("Extras cannot be obtained. PLease make sure that the lib directory lies in the same directory as Tickler Jar file and contains dex2jar-2.0 folder"); return commands; } } public ArrayList queryUrisFromSmali(){ return this.provAtt.queryUrisFromSmali(TicklerVars.extractedDir); } public ArrayList getContentUriFromDex(String dexPath) { provAtt.getContentURIsFromSmali(dexPath); return provAtt.queryContents(); } public void prepareProviderAttacks(String dexPath) { this.setManifestPath(dexPath); this.provAtt.getContentURIsFromSmali(dexPath); } public String getManifestPath() { return manifestPath; } public void setManifestPath(String manifestPath) { this.manifestPath = manifestPath; } ////// Get Extras ////// private String getExtrasOfComp(IComponent comp){ String extrasLine = " "; ExtrasUtil cU = new ExtrasUtil(); extrasLine = cU.getExtras(comp.getName()); return extrasLine; } private ArrayList addExtrasToCommands(String extrasLine, ArrayList commands ) { ArrayList additionalCommands = new ArrayList<>(); for (String cmd : commands){ additionalCommands.add(cmd + extrasLine); } commands.addAll(additionalCommands); return commands; } /** * Adds extras commands if exist * @param comp * @param commands * @return */ private ArrayList addExtrasOfComp(IComponent comp,ArrayList commands) { String extrasLine = this.getExtrasOfComp(comp); if (!extrasLine.equals(" ")) return this.addExtrasToCommands(extrasLine, commands); return commands; } } ================================================ FILE: src/main/java/base/Base64Util.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package base; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import commandExec.Commando; import initialization.TicklerVars; /** * Find Base64 values in a directory * @author aabolhadid * */ public class Base64Util { private FileUtil fU; private SearchUtil searcher; public Base64Util(){ this.searcher = new SearchUtil(); this.fU = new FileUtil(); } /** * @param dir * @param key * @return */ public ArrayList searchB64inDir(String dir, String key){ String keyLower = key.toLowerCase(); ArrayList results = new ArrayList<>(); List files = this.searcher.search4FileInDir(dir, null); for (File f : files){ String decoded = this.fileToBase64(f.getAbsolutePath()); if (decoded.toLowerCase().contains(keyLower)){ results.add(f.getAbsolutePath()); } } return results; } /** * Searches for a key in the DataDir of an app * @param key * @return Names of the files that contain the base64 value of the key */ public ArrayList searchB64DataDir(String key){ return this.searchB64inDir(TicklerVars.dataDir, key); } /** * Check first if the file is not empty!!!, then read the file and base64 Dec every line * @param filePath * @return */ public String fileToBase64(String filePath){ String theFile="", decLine, returnString=""; String ls = System.getProperty("line.separator"); try{ theFile=this.fU.readFile(filePath); } catch(IOException e){ e.printStackTrace(); } if (!theFile.isEmpty()){ String[] fileArray= theFile.split(ls); for (String s:fileArray){ decLine= this.breakLineBase64Dec(s); if (!decLine.isEmpty()|| !decLine.matches("^\\s*$")) returnString+= decLine+ls; } } return returnString; } /** * Break every line with delimiter of non-char or non-digit (except for = ??) then decode every part * @param line * @return */ private String breakLineBase64Dec(String line){ String[] broken = line.split("[^\\w\\d=]"); String dec="", returnString="", asciiDec=""; for (String s: broken){ dec = this.getBase64Dec(s); asciiDec = this.getAsciiFromString(dec); if (!asciiDec.isEmpty()) returnString+=asciiDec+" "; } return returnString; } public String getAsciiFromString(String complex){ String pure = complex.replaceAll("[^\\x20-\\x7F]", ""); return pure; } /** * Base64 decodes a String * @param orig * @return */ public String getBase64Dec(String orig){ byte[] dec = org.apache.commons.codec.binary.Base64.decodeBase64(orig); return new String(dec); } } ================================================ FILE: src/main/java/base/CopyUtil.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package base; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import cliGui.OutBut; import commandExec.Commando; import info.InfoGathering; import initialization.TicklerConst; import initialization.TicklerVars; public class CopyUtil { private FileUtil fileTrans; public CopyUtil() { this.fileTrans = new FileUtil(); } /////////////////////////////////// Copy Data //////////////////////////////////////////////// /** * Updates local and external data directory */ public void copyStorage() { this.copyDataDir(); this.copyExtDir(TicklerVars.extDataDir); } /** * Copy local and external storage directories to their usual directories * OR copy them to Transfers directory * @param dest */ public void copyStorage(String dest) { if (dest == null) { this.copyStorage(); } else { String mainDest =TicklerVars.transferDir+dest+"/"; this.copyDataDir(mainDest+TicklerConst.DATA_DIR_NAME); this.copyExtDir(mainDest+TicklerConst.EXTERNAL_STORAGE_Dir); } } /** * Copies DAta directory from the device, replaces any space in any file or dir name with __ */ public void copyDataDir() { this.copyDataDir(TicklerVars.dataDir); } /** * Copy local Data Directory to a specific destination * @param dest */ public void copyDataDir(String dest){ String src = "/data/data/"+TicklerVars.pkgName; if (this.fileTrans.isExistOnDevice(src)) { this.fileTrans.warnOverrideAndDelete(dest); System.out.println("\n!!! NOTE: Space in Files' and Directories' names are replaced by two underscores __ !!!\n"); this.fileTrans.copyDirToHost(src, dest,false); //Escape space in file names this.fileTrans.escapeSpaceInDir(new File(dest)); } else { OutBut.printError("Data Directory does not exist on the device"); } } /** * Copy local data storage directory to a specific destination * I think it's duplicated and the code changed anyway to copy both local and ext storage * @param name */ /* public void copyDataDirName(String name){ String dest; if (name==null){ dest = TicklerVars.dataDir; } else { dest =TicklerVars.transferDir+name; // FileUtil fU = new FileUtil(); this.fileTrans.createDirOnHost(TicklerVars.transferDir); } this.copyDataDir(dest); } */ // Copy any file or directory from the device to the host //Create a new directory for each transfer public void copyToHost(String src, String dest){ String timestamp = new SimpleDateFormat("dd-MM-yy_HH.mm.ss").format(new Date()); String srcName = fileTrans.getFileNameFromPath(src); String dstDirName; if (dest == null){ dstDirName = srcName+"_"+timestamp; } else { dstDirName = dest; } String destDir = TicklerVars.transferDir+dstDirName; this.fileTrans.copyDirToHost(src, destDir,false); //Check if (new File(destDir).exists()) System.out.println(src+" has been copied successfully to "+destDir); } /** * Copy External storage directory to a certain destination * @param destExtDir */ public void copyExtDir(String destExtDir) { InfoGathering info = new InfoGathering(); // FileUtil fU = new FileUtil(); String extDir = info.getSdcardDirectory().replaceAll("\\n", ""); // String destExtDir=TicklerVars.transferDir+TicklerConst.COPIED_EXTERNAL_STORAGE_NAME; if (!extDir.isEmpty()){ OutBut.printStep("Copying External Storage Directory: "+extDir+"\n"); this.fileTrans.copyDirToHost(extDir, destExtDir,false); System.out.println(""); } } } ================================================ FILE: src/main/java/base/DOMXMLReader.java ================================================ package base; import java.io.File; import java.io.IOException; import java.util.ArrayList; import javax.xml.crypto.Data; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import cliGui.OutBut; import components.Manifest; import components.Permission; import components.Provider; import components.Receiver; import components.Service; import components.UsesPermission; import components.Action; import components.Activity; import components.Application; import components.Category; import components.DataUri; import components.IComponent; import components.Intent; // Following https://www.journaldev.com/898/read-xml-file-java-dom-parser public class DOMXMLReader { private String manifestPath; private Manifest manifest; public static void main(String[] args) { DOMXMLReader r = new DOMXMLReader("/home/a7mad/Documents/Mobiles/Android/AndroTickler/eclipseTickler/insecManTest.xml"); r.parselManifest(); } public DOMXMLReader(String manifestFile) { this.manifestPath = manifestFile; this.manifest = new Manifest(); } /** * Parse the Manifest File in general */ public Manifest parselManifest() { File manifestFile= new File(this.manifestPath); DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder; try { dBuilder = dbFactory.newDocumentBuilder(); Document doc = dBuilder.parse(manifestFile); doc.getDocumentElement().normalize(); this.parseManDoc(doc); return this.manifest; } catch (ParserConfigurationException | SAXException | IOException e) { e.printStackTrace(); } return null; } private void parseManDoc(Document doc) { ArrayList usesPerm = new ArrayList(); ArrayList perms = new ArrayList(); Application app = new Application(); NodeList nl; //Application, components and Intent filters nl = doc.getElementsByTagName("application"); for (int i=0;i actList = new ArrayList(); ArrayList serList = new ArrayList<>(); ArrayList provList = new ArrayList<>(); ArrayList recList = new ArrayList(); Application app = new Application(); NodeList nl; app.setAllowBackup(true); app.setDebuggable(false); if (node.getNodeType()==Node.ELEMENT_NODE) { Element el = (Element) node; if (el.getAttribute("android:allowBackup").toLowerCase().equals("false")) app.setAllowBackup(false); if (el.getAttribute("android:debuggable").toLowerCase().equals("true")) app.setDebuggable(true); app.setName(el.getAttribute("android:name")); //Components nl = el.getElementsByTagName("activity"); for (int i=0;i intFilList; if (node.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element) node; compy.setName(element.getAttribute("android:name")); compy.setPermission(element.getAttribute("android:permission")); //Intent Filters nl = element.getElementsByTagName("intent-filter"); intFilList = new ArrayList(); for (int i=0;i actionList; ArrayList catList; ArrayList dataList; if (node.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element) node; //Actions nl = element.getElementsByTagName("action"); actionList = new ArrayList(); for (int i=0;i(); for (int i=0;i(); for (int i=0;i listFilesInDir(String dirLoc){ ArrayList fileList = new ArrayList(); try { Path source = Paths.get(dirLoc); Files.walk(source).filter(Files::isRegularFile).forEach(p -> fileList.add(this.pathToFile(p))); } catch (IOException e) { e.printStackTrace(); } return fileList; } private File pathToFile(Path p) { File f=p.toFile(); if (f.exists()) { return f; } return null; } /** * Search for files with specific extensions or contain a specific string. * Solving FileUtils issues * @param dirLoc * @param exts * @return */ public ArrayList listFilesInDirContain(String dirLoc,String[] exts) { ArrayList filez = this.listFilesInDir(dirLoc); ArrayList returnFilez = new ArrayList(); for(File f : filez) { for(String ext:exts) if (f.getName().contains(ext)) { returnFilez.add(f); } } return returnFilez; } ////////////// Android Device //////////////// public void copyOnDevice(String src, String dest) { String command = "cp -fr "+src+" "+dest; this.commando.execRoot(command); } public void copyToDevice(String src, String dest) { String fName = this.getFileNameFromPath(src); // this.warnOverrideAndDelete(dest+fName); String sdCardDestPath = TicklerVars.sdCardPath+fName; String command = "adb push "+src+" "+sdCardDestPath; int pullResult=this.commando.executeProcessForAdbPull(command); this.copyOnDevice(sdCardDestPath, dest); // this.deleteDirFromDevice(src); } public void pullFromSDcard(String src, String dest) { String fName = this.getFileNameFromPath(src); this.warnOverrideAndDelete(dest+fName); String command = "adb pull "+src+" "+dest; int pullResult=this.commando.executeProcessForAdbPull(command); this.deleteDirFromDevice(src); } public String createDirOnDevice(String path) { String command = "mkdir -p "+path; return this.commando.execRoot(command); } public String deleteDirFromDevice(String path) { String command = "rm -fr "+path; return this.commando.execRoot(command); } //Checks if the file exists on the android device public boolean isExistOnDevice(String path) { String command = "ls "+path; String op = this.commando.execRoot(command); if (op.toLowerCase().contains("no such file or directory")) return false; return true; } private void b4CopyChecks(String path) throws TNotFoundEx{ if (!this.isExistOnDevice(path)){ throw new TNotFoundEx("!!!!!! ERROR: File / Directory does not exist on the device"); } } /////////////////////////////////////// Between Device and host ////////////////////////////////// /** * Copies a file / directory from any location on the device to the host (linux) through SDcard * @param silent if true then don't print a message */ public void copyDirToHost(String src,String dest,boolean silent) { try{ if (!silent) OutBut.printStep("Copying: "+src+" to ...\n"+dest+" ......"); this.b4CopyChecks(src); String srcName = this.getFileNameFromPath(src); this.deleteDirFromDevice(TicklerVars.sdCardPath+srcName); this.copyOnDevice(src, TicklerVars.sdCardPath); this.prepareDestination(dest); this.pullFromSDcard(TicklerVars.sdCardPath+srcName, dest+"/"); File f = new File(TicklerVars.sdCardPath+srcName); //Clean (uncommented) this.deleteDirFromDevice(TicklerVars.sdCardPath+srcName); } catch(TNotFoundEx e) { System.out.println(e.getMessage()); } } /////////////// I/O /////////////// public String readFile(String file) throws IOException { BufferedReader reader = new BufferedReader(new FileReader (file)); String line; StringBuilder fileString = new StringBuilder(); String ls = System.getProperty("line.separator"); try { while((line = reader.readLine()) != null) { fileString.append(line); fileString.append(ls); } return fileString.toString(); } finally { reader.close(); } } public void writeFile(String fileName, String content){ try { BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); writer.write(content); writer.close(); } catch(IOException e) { e.printStackTrace(); } } /////// Preparation of Transfer directory public String prepareTimestampTransfer(){ String timestamp = new SimpleDateFormat("dd-MM-yy_HH.mm.ss").format(new Date()); if (!this.isExist(TicklerVars.transferDir)) this.createDirOnHost(TicklerVars.transferDir); return timestamp; } private void prepareDestination(String dst) { File destFile = new File(dst); this.createDirOnHost(dst); } } ================================================ FILE: src/main/java/base/JsonParser.java ================================================ package base; import java.io.File; import java.io.FileReader; import java.text.ParseException; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import cliGui.OutBut; public class JsonParser { private JSONParser parser; private String squeezeFileLoc; public static void main(String[] args) { JsonParser jp = new JsonParser(); // jp.parseJsonFile(); String objo = "{ \"squeeze\": [{\"Title\":\"World accessible files\",\"Values\":[ \"MODE_WORLD_READABLE\", \"MODE_WORLD_WRITABLE\"]}," + "{\"Title\":\"WebView\",\"Values\":[ \"addJavascriptInterface\", \"setAllowContentAccess\", \"setAllowFileAccess\", \"setAllowUniversalAccess\" ]}]}"; ArrayList>> arr = jp.parseJsonString(objo); // System.out.println("--"+title+"--"); // for (int j=0;j> e : arr) { System.out.println(e.getKey()); OtherUtil.printStringArray(e.getValue()); } } public JsonParser() { parser = new JSONParser(); //For now squeezeFileLoc="/home/a7mad/eclipse-workspace/TicklerV2.1/bin/Squeeze.conf"; } private void parseJsonFile() { try { // String objo = "\"squeeze\":[{\"a\":\"aa\",\"b\":\"bb\"}]}"; String objo = "{ \"squeeze\": [{\"Title\":\"World accessible files\",\"values\":[ \"MODE_WORLD_READABLE\", \"MODE_WORLD_WRITABLE\"]}," + "{\"Title\":\"WebView\",\"values\":[ \"addJavascriptInterface\", \"setAllowContentAccess\", \"setAllowFileAccess\", \"setAllowUniversalAccess\" ]}]}"; // Object objy = parser.parse(new FileReader(this.squeezeFileLoc)); Object objy = parser.parse(objo); JSONObject jsonObj = (JSONObject) objy; // JSONArray arr = (JSONArray) objy; JSONArray arr = (JSONArray)jsonObj.get("squeeze"); System.out.println(arr.size()); Iterator itr1 = arr.iterator(); Iterator itr2 = arr.iterator(); // while (itr2.hasNext()) // { // itr1 = ((Map) itr2.next()).entrySet().iterator(); // while (itr1.hasNext()) { // Map.Entry pair = itr1.next(); // System.out.println(pair.getKey() + " : " + pair.getValue()); // } // } String test=""; for (int i=0;i>> parseJsonString(String jsonString) { // String jsonString = "{ \"squeeze\": [{\"Title\":\"World accessible files\",\"Values\":[ \"MODE_WORLD_READABLE\", \"MODE_WORLD_WRITABLE\"]}," // + "{\"Title\":\"WebView\",\"Values\":[ \"addJavascriptInterface\", \"setAllowContentAccess\", \"setAllowFileAccess\", \"setAllowUniversalAccess\" ]}]}"; String jsonStr="",title=""; JSONObject jsObj2; JSONArray valuesJA; SimpleEntry> sEnt; // ArrayList>> squeezeParams= new ArrayList<>(); ArrayList>> squeezeParams= new ArrayList<>(); JSONObject rootJO = this.parseStringToObj(jsonString); JSONArray squeezeJA = (JSONArray)rootJO.get("squeeze"); for (int i=0;i aL= (ArrayList)valuesJA; sEnt=new SimpleEntry<>(title,aL); squeezeParams.add(sEnt); } return squeezeParams; } private JSONObject parseStringToObj(String jsonStr) { try { Object objy = parser.parse(jsonStr); JSONObject jsonObj = (JSONObject) objy; return jsonObj; } catch(Exception e) { OutBut.printError("Error while Parsing Squeeze JSON Configuration"); e.printStackTrace(); return null; } // return jsonObj; } } ================================================ FILE: src/main/java/base/OtherUtil.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package base; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.AbstractMap.SimpleEntry; import java.util.regex.Matcher; import java.util.regex.Pattern; import cliGui.OutBut; import initialization.TicklerVars; /** * Mainly regex operations now * @author aabolhadid * */ public class OtherUtil { /** * Gets all instances that match a regex in a string * @param s * @param regex * @return */ public static ArrayList getRegexFromString(String s, String regex){ ArrayList result = new ArrayList<>(); Pattern p = Pattern.compile(regex); Matcher m = p.matcher(s); try{ while(m.find()) result.add(m.group(1)); } catch(IndexOutOfBoundsException ex) { } return result; } /** * Gets all instances that match a regex in a string * @param s * @param regex * @return */ public static boolean isRegexInString(String s, String regex){ Pattern p = Pattern.compile(regex); Matcher m = p.matcher(s); if (m.matches()) return true; return false; } public static ArrayList removeDuplicates(ArrayList orig){ return new ArrayList(new LinkedHashSet(orig)); } public static String pressAnykey(){ BufferedReader is = new BufferedReader(new InputStreamReader(System.in)); System.out.println("Press Enter to continue, or enter snapshot to take a snapshot ........"); return OtherUtil.readInput(); } public static String pressAnyKeySilent(){ BufferedReader is = new BufferedReader(new InputStreamReader(System.in)); return OtherUtil.readInput(); } private static String readInput(){ BufferedReader is = new BufferedReader(new InputStreamReader(System.in)); String key=""; try { key = is.readLine(); } catch (IOException e) { e.printStackTrace(); } return key; } /** * As in different functions in Searcher * @param hits ARraylist of SimpleEntry, obtained after searching for a key * @param toBeReplaced Replace the long path name, such as TicklerVars.jClassDir * @param replacement Replacement such as [Data_Dir] */ public static void printSimpleEntryArray(ArrayList hits, String toBeReplaced, String replacement) { if (!hits.isEmpty()){ OutBut.printNormal(hits.size()+" Search Results are found :\n===============================\n"); for (SimpleEntry e: hits){ String filePath = e.getKey().toString().replaceAll(toBeReplaced, replacement); System.out.println("#FileName: "+filePath); System.out.println(" "+e.getValue()+"\n"); } OutBut.printStep("Where "+replacement+" is "+toBeReplaced); } } /** * Corrects a path if ~ is used * @param path * @return */ public static String getAbsolutePath(String path){ String codeRootNotHome=path.replace("~", System.getProperty("user.home")); File cR = new File(codeRootNotHome); if (cR.exists()){ return codeRootNotHome; } return null; } public static void printStringArray(ArrayList aL){ for (String s:aL) OutBut.printNormal(s); } } ================================================ FILE: src/main/java/base/SearchUtil.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package base; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.util.ArrayList; import java.util.List; import java.util.AbstractMap.SimpleEntry; import java.util.regex.Matcher; import java.util.regex.Pattern; //import org.apache.commons.io.FileUtils; import cliGui.OutBut; import commandExec.Commando; import initialization.TicklerVars; public class SearchUtil { /** * Search for a key in a directory * @param path * @param key * @return ArrayList All lines containing this key */ public ArrayList search4KeyInDir(String path, String key){ List files = this.search4FileInDir(path, null); ArrayList hits = new ArrayList(); for (File f : files){ ArrayList results = this.findInFile(f, key); hits.addAll(results); } return hits; } /** * Searches for a key in a directory and returns the hits and their file names * @param path * @param key * @return ArrayList of all lines containing this key and the file name of each hit */ public ArrayList search4KeyInDirFName(String path,String key){ ArrayList hits = new ArrayList<>(); List files = this.search4FileInDir(path, null); for (File f : files){ ArrayList results = this.findInFile(f, key); //Print file path instead of just the name (better results for obfuscated code) String fName = f.getAbsolutePath(); for (String s:results){ SimpleEntry e = new SimpleEntry(fName, s); hits.add(e); } } return hits; } /** * Squeeze code located in a custom location * @param key * @param codeLoc * @return */ public ArrayList searchForKeyInJava(String key, String codeLoc){ ArrayList hits = new ArrayList<>(); List files = this.search4FileInDir(codeLoc, null); for (File f : files){ String fName = f.getAbsolutePath(); if (!fName.contains("com/google") && !fName.contains("android/support")){ ArrayList results = this.findInFile(f, key); for (String s:results){ SimpleEntry e = new SimpleEntry(fName, s); hits.add(e); } } } return hits; } public ArrayList searchForKeyInJava(String key){ return this.searchForKeyInJava(key, TicklerVars.jClassDir); } /** * After searching for a key in files (which is faster), Refine the search by searching for a regex in the first search's result. * @param eArray * @param regex * @return */ public ArrayList refineSearch(ArrayList eArray, String regex){ ArrayList result = new ArrayList<>(); ArrayList regexResult = new ArrayList<>(); for (SimpleEntry e: eArray){ regexResult = OtherUtil.getRegexFromString(e.getValue().toString(), regex); if (!regexResult.isEmpty()) result.add(e); } return result; } /** * USed with multiline comments disclosure for now. * * @param eArray * @param regex * @return */ public ArrayList refineSearchMatch(ArrayList eArray, String regex){ ArrayList result = new ArrayList<>(); for (SimpleEntry e: eArray){ if (OtherUtil.isRegexInString(e.getValue().toString(), regex)) result.add(e); } return result; } /** * Search for extensions in a directory (or list all files in directory) * @param path * @param extensions filter by extension types * @return */ public List search4FileInDir(String path, String[] extensions){ FileUtil fU = new FileUtil(); // File dir = new File(path); List files; if (extensions != null){ // files = (List)FileUtils.listFiles(dir, extensions, true); files = fU.listFilesInDirContain(path, extensions); } else { // files = (List)FileUtils.listFiles(dir, null, true); files = fU.listFilesInDir(path); } return files; } /** * Search for a file on the host * @param path * @param key * @return */ public String searchOnDevice(String path, String key){ String command = "find "+path+ " -name "+key; Commando commando = new Commando(); String result = commando.execRoot(command); return result; } /** * Search for a key in a file (without regex) * @param f * @param key * @return */ public ArrayList findInFile(File f, String key){ return this.searchInFile(f, key, false); } /** * Search for a regex in a file * @param f * @param regex * @return */ public ArrayList findRegexInFile(File f, String regex){ return this.searchInFile(f, regex,true); } /** * Searches for a key in a file, whether it is a normal string or a regex * The search is CASE INSENSITIVE * The main function for findInFile and findRegexInFile * @param f * @param key * @param regex true: search for regex, false: search for a String * @return */ private ArrayList searchInFile(File f, String key,boolean regex){ ArrayList results = new ArrayList(); String line; try { BufferedReader reader = new BufferedReader(new FileReader(f)); while ((line =reader.readLine())!= null) { if (this.checkLineAndRegex(line,key,regex)) results.add(line); } reader.close(); } catch (Exception e) { e.printStackTrace(); } return results; } /** * Searches for a string in a line (CASE INSENSITIVE), or for a regex in a line * Considered the main check of findInFile and FindRegexInFile * @param line * @param key * @param regex * @return */ private boolean checkLineAndRegex(String line, String key, boolean regex){ boolean result; //Search with regex if (regex){ Matcher m = Pattern.compile(key).matcher(line); result = m.matches(); } //Normal contains() else { result = line.toLowerCase().contains(key.toLowerCase()); } return result; } } ================================================ FILE: src/main/java/base/Tickler.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package base; import java.io.File; import java.util.ArrayList; import org.apache.commons.codec.binary.StringUtils; import actions.Comparer; import actions.Searcher; import actions.Snapshots; import apk.Decompiler; import apk.newApks.CreateApk; import attacks.ActivityStarter; import attacks.Broadcaster; import attacks.StartAttack; import cliGui.OutBut; import code.JavaSqueezer; import components.IActivityService; import components.IComponent; import components.Manifest; import components.Receiver; import db.DatabaseTester; import device.Packagez; import exceptions.TNotFoundEx; import frida.FridaCli; import info.InfoGathering; import info.InfoGatheringReporting; import info.ListComponents; import initialization.TicklerChecks; import initialization.TicklerConst; import initialization.TicklerVars; import manifest.ManifestDealer; public class Tickler { private Manifest manifest; private ManifestDealer dealer; private ActivityStarter actAttacker; private boolean isLog; private StartAttack startAttack; private DatabaseTester dbTest; private CreateApk newApk; private Searcher searcher; private CopyUtil copyz; private Snapshots snaps; private Comparer comps; // If offline not compatible --> raise exception when initalizeTicklerNoDevice runs (not to fuck up the whoile code) private boolean isOfflineCompatible = true; private boolean isDev=true; /** * * @param mode: Tickler's operation mode * - pkg: Package mode (app) and device needed * - offline: Package mode and device is not needed * - noPkg: App not-related ops, such as generic snapshot * @param pkgName */ public Tickler(String mode, String pkgName) { this.inits(); // if(mode.equals("pkg")){ // this.ticklerPackageInit(pkgName); // } // else if (TicklerVars.pkgName == null){ // TicklerChecks tc = new TicklerChecks(); // tc.loadConfiguration(); // TicklerVars.updateVars("NoPackage"); // } if (mode.equals("noPkg")) { TicklerChecks tc = new TicklerChecks(); tc.loadConfiguration(); TicklerVars.updateVars("NoPackage"); return; } else if (mode.equals("offline")) { this.isDev= false; } //else this.ticklerPackageInit(pkgName, this.isDev); } /** * Instantiates necessary classes to start Tickler */ private void inits() { this.copyz = new CopyUtil(); this.snaps = new Snapshots(); this.comps = new Comparer(); } /** * Initiates Tickler in Package mode, whether online or offline * @param pkgName * @param isDev */ // For now isDev = true --> work online mode: needs a device alwasys connected private void ticklerPackageInit(String pkgName,boolean isDev){ try{ this.runTicklerChecks(pkgName, isDev); this.dealer = new ManifestDealer(); this.dealer.meetThePackage(pkgName); if (!this.dealer.wasApkExist()) this.copyDataDir(null); } catch(TNotFoundEx e) { OutBut.printError(e.getMessage()); System.exit(0); } } private void runTicklerChecks(String pkgName, boolean isDev) throws TNotFoundEx { TicklerChecks tc = new TicklerChecks(); if (isDev) tc.initiaizeTickler(pkgName); else tc.initalizeTicklerNoDevice(pkgName); } //////// Start components ////////// /** * Start components (All vs. comp type, exported vs. all) * @param compType * @param exported */ public void start(int compType, boolean exported){ this.prepareComponentAttack(); this.triggerGroup(compType, exported); } /** * Triggers groups of components (all or just one type, exported or not) * @param compType * @param exported */ private void triggerGroup(int compType, boolean exported) { //1- Get components based on type and exported values ArrayList components = this.dealer.getComponentsOfType(compType, exported); //2- Get commands strings ArrayList commands = this.startAttack.attackComponents(components); //3- Add Prov commands if ((compType == TicklerConst.PROVIDER || compType == TicklerConst.ALLCOMPS) && !exported){ commands.addAll(this.startAttack.queryUrisFromSmali()); } //4- Start commands this.startAttack.executeTriggerCommands(commands, exported); //5- Stop logger this.startAttack.stopLogging(); } /** * Trigger a component by name * @param compName */ public void attackComponent(String compName){ this.prepareComponentAttack(); if (this.dealer.isComponentExist(compName)) { IComponent comp = this.dealer.getComponentByName(compName); ArrayList commands = this.startAttack.attackComponent(comp); this.startAttack.executeTriggerCommands(commands, false); } else { OutBut.printError("No component of name "+compName+" found in the Manifest file\n" + "Use -l command to display all components of this app"); } this.startAttack.stopLogging(); } /** * Preparations before any kind of component triggering */ public void prepareComponentAttack() { //1- Analyze manifest and get the components this.dealer.analyzeManifest(TicklerVars.tickManifestFile); //2- Instantiate attacker this.startAttack = new StartAttack(); //3- Get Launchers but removed //4- Set logger to True, in order to write the command in the file this.startAttack.setLogger(this.isLog); //5- Init TicklerGeneral.schemes to complete dataURI Intents TicklerGeneral.schemes = new ArrayList(); } ///////////// copy ///////////////// /** * Copy local and external data directories of the app to the Tickler folder * @param name */ public void copyDataDir(String name){ copyz.copyStorage(name); // copyz.copyDataDirName(name); // copyz.copyDataDir(name+"/DataDir"); // copyz.copyExtDir(name+"/ExtDataDir"); } //Copy any file / directory from the device to the host public void copyToHost(String src, String dest){ copyz.copyToHost(src, dest); } ///////////////////////////// Databases ////////////////////////// /** * Handles DB actions (check encryption, list DB, search) * @param param Database action * @param isCopy if false, don't update DataDir */ public void databases(String param,boolean isCopy){ this.dbTest= new DatabaseTester(); // Update DataDir? if (isCopy) { // Update DataDir whether it exists or not OutBut.printStep("Updating Data Directory"); this.copyDataDir(null); } this.dbTest.dbOption(param); } //////////////////// Other functions ////////////////// public void snapshot(){ this.snaps.takeSnapshot(); } /** * Create a debuggable version of the app */ public void createDebuggable(){ this.newApk = new CreateApk(TicklerConst.debuggable); this.newApk.createNewApk(); } /** * Solves the MitM issue with Android Nougat (netsecConfiguration) */ public void createNougatMitM(){ this.newApk = new CreateApk(TicklerConst.mitm); this.newApk.createNewApk(); } public void createCustomAPK(String dir, String name){ this.newApk = new CreateApk(10); this.newApk.createAnyApk(dir, name); } public void decompileApk(){ Decompiler d2j = new Decompiler(); d2j.dex2jar(); } /** * Copies the directory of background images from the android device to the host */ public void backgroundSnapshots(){ this.snaps.getBackGroundSnapshots(); } //Copy data directory twice, while you can go crazy in between. Then compares the 2 directories public void diffDataDir(String detailed){ OutBut.printH1("Data Directory changes"); if (detailed == null) this.comps.diff(false); else if (detailed.equalsIgnoreCase("detailed")|| detailed.equalsIgnoreCase("d")) this.comps.diff(true); else{ System.out.println("!!!!!!! WARNING: unknown parameter '"+detailed+"'. \n" + "!!!!!!! Showing only the names of the changed files\n" + "!!!!!!! For detailed comparison use -diff detailed or -diff d flag\n"); this.comps.diff(false); } } /** * Print version number */ public void version(){ OutBut.printNormal("Tickler version: "+TicklerConst.version); } ///////////////////// List //////////////////// public void list(int compType, boolean exported,boolean details){ ListComponents list = new ListComponents(); list.listThis(compType, exported,details); } public void listComponent(String compName){ ListComponents list = new ListComponents(); list.listComponent(compName); } ///////////// General Information ///////////// public void informationGathering() { InfoGatheringReporting infoRep = new InfoGatheringReporting(); infoRep.report(); } public void printPackages(){ Packagez pkgz = new Packagez(); pkgz.printInstalledPkgs(); } public void squeezeCode(String codeLoc){ JavaSqueezer disc = new JavaSqueezer(); if (codeLoc != null && codeLoc.equals("short")){ int index1 = TicklerVars.pkgName.indexOf('.')+1; String subloc= TicklerVars.pkgName.substring(0,TicklerVars.pkgName.indexOf('.', index1)); codeLoc = TicklerVars.jClassDir+"/"+subloc.replaceAll("\\.", "/"); } disc.report(codeLoc); } //Neglecting jsonFile at the moment public void squeezeJSON(String jsonLoc) { JavaSqueezer disc = new JavaSqueezer(); disc.squeezeJson(jsonLoc); } /////////////// Search ////////////////// public void searchPackage(String key){ Packagez pkgz = new Packagez(); pkgz.searchPackage(key,true); } public void searchInCode(String key,String codeLoc){ this.searcher = new Searcher(); this.searcher.scCustomCodeLoc(key, codeLoc); // this.searcher.sC(key,false); } public void searchInCodeAll(String key){ this.searcher = new Searcher(); this.searcher.sC(key,true); } public void searchInDataDir(String key, boolean isCopy){ this.searcher = new Searcher(); this.searcher.searchForKeyInDataDir(key,isCopy); } public void b64Search(String key) { Base64Util b64 = new Base64Util(); ArrayList result = b64.searchB64inDir(TicklerVars.dataDir, key); for (String s : result) OutBut.printNormal(s); } public boolean isLog() { return isLog; } public void setLog(boolean log) { this.isLog = log; FileUtil ft=new FileUtil(); ft.createDirOnHost(TicklerVars.logDir); } ///////////////////////////// Frida ////////////////////////////// public void frida(String[] args, boolean reuse){ FridaCli fridaCli = new FridaCli(); fridaCli.fridaThis(args, reuse); } } ================================================ FILE: src/main/java/base/TicklerGeneral.java ================================================ package base; import java.util.ArrayList; public class TicklerGeneral { //Static Arraylist containing all Data URI schemes. Used to replace $scheme in intents public static ArrayList schemes; } ================================================ FILE: src/main/java/base/XMLReader.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package base; import java.awt.List; import java.io.File; import java.util.ArrayList; import javax.xml.bind.JAXBContext; import javax.xml.bind.Unmarshaller; import javax.xml.bind.UnmarshallerHandler; import attacks.ActivityStarter; import attacks.Broadcaster; //import brut.androlib.ApktoolProperties; import components.Manifest; /** * Marshal and Unmarshal Manifest file * Unused since v2.3 * @author aabolhadid * */ public class XMLReader{ private String manifestFile; private Manifest manifest; public XMLReader(String manifestFile) { this.manifestFile = manifestFile; this.unmarshalManifest(); } public void unmarshalManifest() { Manifest man=new Manifest(); File manifest = new File(this.manifestFile); try { JAXBContext context = JAXBContext.newInstance(Manifest.class); Unmarshaller jaxbUnmarshaller = context.createUnmarshaller(); man = (Manifest) jaxbUnmarshaller.unmarshal(manifest); } catch(Exception e) { System.out.println("ERROR: Manifest cannot be parsed"); e.printStackTrace(); } this.manifest = man; } public Manifest getManifest() { return this.manifest; } } ================================================ FILE: src/main/java/cliGui/OutBut.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package cliGui; public class OutBut { // Colors private static final String ANSI_RESET = "\u001B[0m"; private static final String ANSI_BOLD = "\u001B[1m"; private static final String ANSI_RED = "\u001B[31m"; private static final String ANSI_BLUE = "\u001B[34m"; public static void printH1(String line){ String opLine = "\n=================================================================\n\t\t"; opLine+=ANSI_BOLD+line+ANSI_RESET; opLine+="\n=================================================================\n"; System.out.println(opLine); } public static void printH2(String line){ System.out.println("\n===================== "+line +" ====================="); } public static void printH3(String line){ int length = line.length(); String underline=""; for (int i=0;i>>>>>>> "+line); } public static void printWarning(String line){ System.out.println("!!!!!!!! WARNING: "+line); } public static void printError(String line){ System.out.println(ANSI_RED+ANSI_BOLD+"XXXXX ERROR: "+line +" XXXXX"+ANSI_RESET); } /** * Currently just prints as syso * @param line */ public static void printNormal(String line){ System.out.println(line); } public static void printH1Blue(String line){ String opLine = ANSI_BLUE+"=============================================\n\t"; opLine+=line; opLine+="\n============================================="+ANSI_RESET; System.out.println(opLine); } } ================================================ FILE: src/main/java/cliGui/TicklerCLI.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package cliGui; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.cli.BasicParser; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.MissingArgumentException; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.UnrecognizedOptionException; import code.JavaSqueezer; import exceptions.TNotFoundEx; import base.FileUtil; import base.OtherUtil; import base.SearchUtil; import base.Tickler; import frida.FridaInit; import frida.FridaEnumerateClasses; import frida.FridaPythonScript; import initialization.TicklerConst; import initialization.TicklerVars; public class TicklerCLI { private Options options; private CommandLineParser parser; public static void main(String[] args) { TicklerCLI cli = new TicklerCLI(); cli.parser = new BasicParser(); cli.options = new Options(); Option findPkg = new Option("findPkg",null, true, "Search for a package name"); Option listPack = new Option("pkgs",null, false,"List all installed packages on this device"); Option pkg = new Option("pkg",null, true, "Name of an installed package"); Option trigger = new Option("t","trigger",false,"Start components"); Option all = new Option("all",null,false,"Start all components"); Option exp = new Option("exp",null,false,"Apply only to Exported components"); Option attackComp = new Option("comp",null, true, "Attack a specific component. The name should be as in the manifest file"); Option help = new Option("h", "help", false, "Print this help menu"); Option activities = new Option("act","activities",false,"Attack all activities"); Option services = new Option("ser","services",false,"Attack all services"); Option providers = new Option("prov","providers",false,"Attack all content providers"); Option receivers = new Option("rec","receivers",false,"Attack all broadcast receivers"); Option list = new Option("l","list",false,"List components"); Option info = new Option("i","info",false,"List information about the app"); Option snap = new Option("screen","screenshot",false,"takes a snapshot of the device's screen"); // Option searchInCode = new Option("sc","searchCode",true,"Search for a specific key in Java code"); Option searchInCodeAll = new Option("sc_all",null,true,"Search for a specific key in Java code"); Option searchInSandbox = new Option("sd","searchDataDir",true, "Search for a key in the Data Dir of an app (Files and DB)"); Option log = new Option("log",null,false,"capture LogCat messages"); Option details = new Option("v",null,false,"List in details"); Option bgSnap = new Option("bg","backGroundSnapshots",false,"Get Background images from the device"); Option debuggable = new Option("dbg","debuggable",false,"Create a debuggable APK"); Option decompile = new Option("decomp",null,false, "decompile apk into java code"); Option version = new Option("version",null,false, "Print version"); Option noCopy = new Option("nu","noUpdate",false, "Doesn't update DataDir before execution (with db and big dataDir)"); Option mitm = new Option("mitm",null,false, "Allows user CA in Android 7"); Option fridaReuse = new Option("reuse",null,false, "Reuse existing Frida JS"); Option offline = new Option("offline",null,false, "Offline Mode, no devices needed to be connected"); Option copy2host = OptionBuilder.create("cp2host"); copy2host.setOptionalArg(true); copy2host.setArgs(2); Option diffDir = OptionBuilder.create("diff"); diffDir.setOptionalArg(true); diffDir.setArgs(2); Option db = OptionBuilder.create("db"); db.setOptionalArg(true); db.setArgs(1); Option dataDir = OptionBuilder.create("dataDir"); dataDir.setOptionalArg(true); dataDir.setArgs(1); Option squeeze = OptionBuilder.create("squeeze"); squeeze.setOptionalArg(true); squeeze.setArgs(1); Option sc = OptionBuilder.create("sc"); sc.setOptionalArg(true); sc.setArgs(2); Option apk = OptionBuilder.create("apk"); apk.setArgs(2); Option frida = OptionBuilder.create("frida"); frida.setOptionalArg(true); frida.setArgs(6); Option squeezeJSON = OptionBuilder.create("sq"); squeezeJSON.setArgs(2); cli.options.addOption(pkg); cli.options.addOption(trigger); cli.options.addOption(all); cli.options.addOption(exp); cli.options.addOption(attackComp); cli.options.addOption(diffDir); cli.options.addOption(db); cli.options.addOption(help); cli.options.addOption(activities); cli.options.addOption(services); cli.options.addOption(providers); cli.options.addOption(receivers); cli.options.addOption(list); cli.options.addOption(info); cli.options.addOption(findPkg); cli.options.addOption(listPack); cli.options.addOption(snap); cli.options.addOption(sc); cli.options.addOption(searchInCodeAll); cli.options.addOption(searchInSandbox); cli.options.addOption(log); cli.options.addOption(bgSnap); cli.options.addOption(debuggable); cli.options.addOption(copy2host); cli.options.addOption(details); cli.options.addOption(dataDir); cli.options.addOption(decompile); cli.options.addOption(squeeze); cli.options.addOption(version); cli.options.addOption(noCopy); cli.options.addOption(mitm); cli.options.addOption(apk); cli.options.addOption(frida); cli.options.addOption(fridaReuse); cli.options.addOption(offline); cli.options.addOption(squeezeJSON); try { CommandLine cl = cli.parser.parse(cli.options, args,false); cli.startTickler(cl); } catch(UnrecognizedOptionException e){ OutBut.printError("Unrecognized option "+e.getOption()); System.out.println(TicklerConst.helpMessage); } catch(MissingArgumentException e){ OutBut.printError("Missing parameter of option "+e.getOption().getOpt()); System.out.println(TicklerConst.helpMessage); } catch(TNotFoundEx te) { OutBut.printError(te.getMessage()); } catch(Exception e){ e.printStackTrace(); } } public void startTickler(CommandLine cl) throws TNotFoundEx{ boolean exported = false; boolean details = false; int target = TicklerConst.ALLCOMPS; String mode = "pkg"; if (cl.hasOption("offline")) { this.checkOfflineFeasibility(cl); mode = "offline"; TicklerVars.isOffline = true; } if (cl.hasOption("h") || cl.hasOption("help")) { System.out.println(TicklerConst.helpMessage); } if (cl.hasOption("findPkg")){ Tickler t = new Tickler("noPkg", "findPkg"); t.searchPackage(cl.getOptionValue("findPkg")); } if (cl.hasOption("pkgs")){ Tickler t = new Tickler("noPkg", "pkgs"); t.printPackages(); } ////////////////////////////////// Pkg ///////////////////////////// if (cl.hasOption("pkg")) { Tickler t = new Tickler(mode, cl.getOptionValue("pkg")); if (cl.hasOption("exp")) exported = true; //Logcat if (cl.hasOption("log")){ t.setLog(true); } else if (cl.hasOption("mitm")){ t.createNougatMitM(); } else if (cl.hasOption("debuggable")){ t.createDebuggable(); } else if (cl.hasOption("apk")){ String[] apkArgs = cl.getOptionValues("apk"); t.createCustomAPK(apkArgs[0], apkArgs[1]); } ///// Targets List args = cl.getArgList(); if (cl.hasOption("act")) target =TicklerConst.ACTIVITY; else if (cl.hasOption("ser")) target = TicklerConst.SERVICE; else if (cl.hasOption("prov")) target = TicklerConst.PROVIDER; else if (cl.hasOption("rec")) target = TicklerConst.RECEIVER; ////////// Actions ///////////// //Trigger if (cl.hasOption("trigger") || cl.hasOption("t") ){ //Trigger a component if (cl.hasOption("comp")){ t.attackComponent(cl.getOptionValue("comp")); } //Component name is added without -comp flag else if (!args.isEmpty()){ t.attackComponent(args.get(0)); } else { t.start(target, exported); } } ////List components else if (cl.hasOption("l")||cl.hasOption("list")){ if (cl.hasOption("v")){ details=true; } //List a specific component if (cl.hasOption("comp")){ t.listComponent(cl.getOptionValue("comp")); } // a component name without -comp flag else if (!args.isEmpty()){ t.listComponent(args.get(0)); } else t.list(target,exported,details); } //Search in Code else if (cl.hasOption("sc") || cl.hasOption("searchCode")){ String[] scArgs = cl.getOptionValues("sc"); String key = scArgs[0]; String loc = null; if (scArgs.length >1){ loc = scArgs[1]; } t.searchInCode(key,loc); } else if (cl.hasOption("sc_all") ) t.searchInCodeAll(cl.getOptionValue("sc_all")); // Update Data directory? boolean isCopy=true; if (cl.hasOption("noUpdate") || cl.hasOption("nu")) isCopy = false; // Search for a key in DataDir files and Unencrypted Databases else if (cl.hasOption("sd")) { t.searchInDataDir(cl.getOptionValue("sd"), isCopy); } //Diff data directory before and after an operation else if (cl.hasOption("diff")) t.diffDataDir(cl.getOptionValue("diff")); //Check encryption of Databases else if (cl.hasOption("db")){ t.databases(cl.getOptionValue("db"),isCopy); // } //Information else if (cl.hasOption("info")) t.informationGathering(); //Disclosure else if (cl.hasOption("squeeze")){ String codeLoc = cl.getOptionValue("squeeze"); t.squeezeCode(codeLoc); } else if (cl.hasOption("sq")){ String jsonLoc = cl.getOptionValue("sq"); t.squeezeJSON(jsonLoc); } else if (cl.hasOption("dataDir")){ String dest = cl.getOptionValue("dataDir"); t.copyDataDir(dest); } else if (cl.hasOption("decomp")) t.decompileApk(); else if (cl.hasOption("frida")){ boolean reuse = false; if (cl.hasOption("reuse")) reuse = true; String[] fridaArgs = cl.getOptionValues("frida"); t.frida(fridaArgs,reuse); } if(cl.hasOption("screen")){ t.snapshot(); } // Background screenshots if (cl.hasOption("bg")){ //t = new Tickler("snapshot", "snapshot"); t.backgroundSnapshots(); } if (cl.hasOption("cp2host")){ // Tickler t = new Tickler("snapshot","shanpshot"); String[] args2 = cl.getOptionValues("cp2host"); String src = args2[0]; String dest; if (args2.length >1){ dest = args2[1]; } else { dest = null; } t.copyToHost(src, dest); } } //////////////////////////// With or without PKG ///////////////////////// Tickler t; //package if (! cl.hasOption("pkg")) { t = new Tickler("noPkg", "snapshot"); // Snapshot, with or without package name if(cl.hasOption("screen")){ t.snapshot(); } // Background screenshots if (cl.hasOption("bg")){ //t = new Tickler("snapshot", "snapshot"); t.backgroundSnapshots(); } if (cl.hasOption("cp2host")){ // Tickler t = new Tickler("snapshot","shanpshot"); String[] args = cl.getOptionValues("cp2host"); String src = args[0]; String dest; if (args.length >1){ dest = args[1]; } else { dest = null; } t.copyToHost(src, dest); } } //Version if (cl.hasOption("version")){ Tickler t2 = new Tickler("noPkg","version"); t2.version(); } } public void executeLogcat(String command, File logFile){ Process process; try { process=Runtime.getRuntime().exec(command); BufferedReader reader = new BufferedReader(new InputStreamReader( process.getInputStream())); PrintWriter writer = new PrintWriter(logFile); String line = ""; while ((line = reader.readLine())!= null) { writer.println("line"); } } catch(Exception e) { e.printStackTrace(); } } /** * Migrated from TicklerChecks not to fuck with the whole code * @param command */ public void checkOfflineFeasibility(CommandLine cli) throws TNotFoundEx { String [] offlineCommands = {"findPkg", "pkgs", "log", "diff", "dataDir", "t", "screen", "bg", "cp2host", "frida"}; for (String opt:offlineCommands) { if (cli.hasOption(opt)) { throw new TNotFoundEx("Tickler command "+opt+" is not compatible with offline mode"); } } } } ================================================ FILE: src/main/java/code/ClassExtras.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package code; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.regex.Matcher; import java.util.regex.Pattern; //import org.apache.commons.io.FileUtils; //import org.apache.commons.io.filefilter.TrueFileFilter; //import org.apache.commons.io.filefilter.WildcardFileFilter; import base.FileUtil; import base.OtherUtil; import base.SearchUtil; import initialization.TicklerVars; /** * EXtract extras from Java classes of every component * @author aabolhadid * */ public class ClassExtras { private String className; private Collection files; private ArrayList types, names, defaults,commands; public ClassExtras(String className){ this.className = className; this.types = new ArrayList<>(); this.names = new ArrayList<>(); this.defaults = new ArrayList<>(); this.commands = new ArrayList<>(); } public void process(){ this.getClassFiles(); for (File f : this.files){ this.getExtrasOfClass(f); } } /** * Gets the files whose names contain the class name */ private void getClassFiles(){ FileUtil fU = new FileUtil(); String[] classNameArr = {this.className}; // this.files = FileUtils.listFiles( new File(TicklerVars.jClassDir), new WildcardFileFilter("*" + this.className + "*"), TrueFileFilter.TRUE); this.files = fU.listFilesInDirContain(TicklerVars.jClassDir, classNameArr); } /** * gets class name and searches for it in the Java class dir. * Then searches in each of these files for the names, types and the default values of each extra * regex *? to make the wildcard not gready and end with the first boundary * @param f * @return */ public void getExtrasOfClass(File file){ ArrayList extras = this.getExtraLines(file, this.className); for (String line : extras){ this.types.addAll(OtherUtil.getRegexFromString(line, "get(\\w+?)Extra")); this.names.addAll( OtherUtil.getRegexFromString(line, "get\\w+?Extra\\(\"(.*?)\"") ); this.defaults.addAll( OtherUtil.getRegexFromString(line, ".*Extra\\(.*,(.*?)\\)") ); } } /** * return lines of the file "file" that contain the pattern get*Extra * @param file * @param className * @return */ private ArrayList getExtraLines(File file, String className){ SearchUtil sr = new SearchUtil(); ArrayList extras = sr.findRegexInFile(file, ".*get(.*)Extra.*"); return extras; } /** * returns the parts of syntax of all extras, but avoiding any duplicates of extras names * @return */ public ArrayList getExtrasCommands(){ ArrayList addedExtras = new ArrayList<>(); for (int i=0; i assign a default value * To avoid errors: defaults have been excluded * @return */ private String getExtraCommand(int i){ String cmd = this.getE(this.types.get(i)); String value=""; value = this.getExtraValue(this.types.get(i)); String command = cmd + " " +this.names.get(i)+" "+value; return command; } /** * Returns the information about Extras for -l command * @return */ public ArrayList getExtrasInfo(){ ArrayList ExtrasInfo = new ArrayList<>(); ArrayList addedExtras = new ArrayList<>(); for (int i=0; i getTypes() { return types; } public void setTypes(ArrayList types) { this.types = types; } public ArrayList getNames() { return names; } public void setNames(ArrayList names) { this.names = names; } public ArrayList getDefaults() { return defaults; } public void setDefaults(ArrayList defaults) { this.defaults = defaults; } } ================================================ FILE: src/main/java/code/ExtrasUtil.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package code; import java.util.ArrayList; import base.FileUtil; import initialization.TicklerVars; /** * Extras in Java code * @author aabolhadid * */ public class ExtrasUtil { private FileUtil fT; public ExtrasUtil(){ this.fT = new FileUtil(); } /** * Checks if the App is already decompiled in the proper directory * @return */ public boolean isJClassDir() { if (this.fT.isExist(TicklerVars.jClassDir)) return true; System.out.println("!! ERROR: Decompiled code does not exist"); return false; } /** * Gets extras of a component * @param className */ public String getExtras(String className){ if (this.isJClassDir()) return this.getExtrasIfCodeExists(className); return "!! ERROR: run dex2jar first"; } /** * Gets the extras syntax of a class in one string (e.g. -e Var1 Val1 --ez Var2 Val2) * @param className */ private String getExtrasIfCodeExists(String className){ String cName = this.getClassNameFromCompName(className); ArrayList extrasCommands = this.prepareExtrasCommands(cName); return this.getExtrasCommandLine(extrasCommands); } private String getClassNameFromCompName(String className){ String cName = className; if (className.contains(".") && className.lastIndexOf(".") != 0){ String[] cNames = className.split("\\."); cName = cNames[cNames.length -1]; } return cName; } /** * Convert an array of extra parameters into one line * @param extrasCommands * @return */ private String getExtrasCommandLine(ArrayList extrasCommands) { String line =" "; for (String cmd : extrasCommands){ line = line+cmd+" "; } return line; } /** * 1- Gets Extras Commands array from ClassExtra class * 2- Check for duplicates * 3- removes L and F from default values * 4- propose default values if there aren't any * @param cName * @return */ private ArrayList prepareExtrasCommands(String cName){ ClassExtras cExtra = new ClassExtras(cName); cExtra.process(); ArrayList allExtras = cExtra.getExtrasCommands(); return allExtras; } /** * Gets Info about Extras for -l command * @return */ public ArrayList getExtrasInfo(String className){ ArrayList extrasInfo = new ArrayList<>(); if (this.isJClassDir()){ String cName = this.getClassNameFromCompName(className); ClassExtras cExtra = new ClassExtras(cName); cExtra.process(); extrasInfo = cExtra.getExtrasInfo(); } return extrasInfo; } } ================================================ FILE: src/main/java/code/JavaSqueezer.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package code; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; import java.util.AbstractMap; import java.util.AbstractMap.SimpleEntry; import java.util.regex.Matcher; import java.util.regex.Pattern; //import org.apache.commons.io.FileUtils; import actions.Searcher; import base.FileUtil; import base.JsonParser; import base.OtherUtil; import base.SearchUtil; import cliGui.OutBut; import info.InfoGathering; import initialization.TicklerVars; /** * Extracts information from code such as log messages, comments, credentials, encryption and strings * @author aabolhadid * */ public class JavaSqueezer { private SearchUtil sU; private String codeRoot; private ArrayList squeezeList; private String jsonSq="{ \"squeeze\": [{\"Title\":\"World accessible files\",\"Values\":[ \"MODE_WORLD_READABLE\", \"MODE_WORLD_WRITABLE\"]}," + "{\"Title\":\"WebView\",\"Values\":[ \"addJavascriptInterface\", \"setAllowContentAccess\", \"setAllowFileAccess\", \"setAllowUniversalAccess\" ]}]}"; private ArrayList stringArrayList; protected PrintStream origSysOut; public JavaSqueezer(){ this.sU = new SearchUtil(); this.codeRoot = TicklerVars.jClassDir; } public void report(String codeRoot){ if (codeRoot != null){ String codeRootNotHome=codeRoot.replace("~", System.getProperty("user.home")); File cR = new File(codeRootNotHome); if (cR.exists()){ this.codeRoot = codeRootNotHome; this.report(); } else { OutBut.printError("The code location you entered "+codeRoot+" does not exist"); } } else { //Redirect system out to a file this.report(); } } public void report(){ //Redirect ot Squeeze out file this.writeSqueezeInFile(); //Preparation: get Strings this.getStringsInCode(); //Components this.libsAndComponents(); this.frameworks(); this.soLibFiles(); this.dllFiles(); //Storage this.storage(); this.externalStorageInCode(); //Communication this.httpUrls(); this.findSchemes(); this.pinning(); //Crypto this.weakCyphers(); this.crypto(); //Disclosure this.commentsInCode(); this.credentialsInCode(); this.logInCode(); this.testDisclosure(); // this.getStringsInCode(); // this.squeezeJSon(); OutBut.printStep("Where [Java_Code_Dir] is "+this.codeRoot); //Back to SYS Out this.backToSystemOut(); //Print Squeeze file this.printSqueezeFile(); } //////////////////////////////// Storage ////////////////////////////////// /** * Search for any possible use of external storage */ public void externalStorageInCode(){ OutBut.printH2("Possible External Storage"); ArrayList eArray = this.sU.searchForKeyInJava("getExternal",this.codeRoot); this.printE(eArray); } private void storage(){ OutBut.printH2("Storage: DBs and shared preferences"); String[] keys = {"sharedpref", "SQLiteDatabase", "CacheDir","AndroidKeyStore", "KeyStore"}; ArrayList eArray = this.returnFNameLineGroup(keys, false); this.printE(eArray); } /////////////////////// Components ////////////////////////// private void libsAndComponents(){ OutBut.printH2("Imports"); ArrayList e = this.sU.searchForKeyInJava("import",this.codeRoot) ; ArrayList e1 = this.sU.refineSearch(e,"(^import.+)") ; ArrayList eResult = this.sU.refineSearch(e1, "^(?:(?!(import\\sjava|import\\sandroid)).)*$"); ArrayList files=this.returnValues(eResult); for (String s:files) OutBut.printNormal(s); OutBut.printH2("Components and libraries"); String[] comps = {"Webview", "Okhttp", "Sqlcipher"}; ArrayList hits = new ArrayList<>(); files = new ArrayList<>(); for (String c:comps){ hits=this.sU.searchForKeyInJava(c,this.codeRoot); if (!hits.isEmpty()){ OutBut.printH3(c); files=this.returnFileNames(hits); for (String s:files) OutBut.printNormal(s); OutBut.printNormal("\n---------------------------------------------------------------"); } } } // private void libFiles(){ // String[] extenstions = {"so"}; // List soLibs = this.sU.search4FileInDir(TicklerVars.extractedDir, extenstions); // if (!soLibs.isEmpty()) { // OutBut.printH2("Library files in the app"); // for (File f:soLibs ){ // OutBut.printNormal(f.getAbsolutePath().replaceAll(TicklerVars.extractedDir, "[Extracted_Apk]")); // } // OutBut.printNormal("\nWhere [Extracted_Apk] is "+TicklerVars.extractedDir); // } // } /** * Searches for so Libraries in the APK */ public void soLibFiles(){ String[] extenstions = {".so"}; OutBut.printH2("Libraries in the APK"); this.searchForFilesInAPK(extenstions); } /** * Searches for DLL files in the APK */ public void dllFiles(){ String[] extenstions = {".dll"}; OutBut.printH2("DLLs in the APK"); this.searchForFilesInAPK(extenstions); } /** * Checks for certificates in APK, taken from -info flag */ private void certsInAPK() { InfoGathering info = new InfoGathering(); info.getCertificatesInApkDirectory(); } private void frameworks() { OutBut.printH2("Frameworks and Protocols"); String[] keys = {"Cordova","PhoneGap", "Xamarin","Corona","Appsee","MQTT", "websocket"}; ArrayList hits = new ArrayList(); ArrayList files = new ArrayList(); for (String c:keys){ hits=this.sU.searchForKeyInJava(c,this.codeRoot); OutBut.printH3(c); if (!hits.isEmpty()){ files=this.returnFileNames(hits); for (String s:files) OutBut.printNormal(s); OutBut.printNormal("\n---------------------------------------------------------------"); } } } /////////////////////// Crypto //////////////////////// /** * Search for weak hashes */ private void weakCyphers() { System.out.println("\n"); String[] weakCrypto = {"Rot13", "MD4", "MD5", "RC2", "RC4", "SHA1"}; OutBut.printH2("Possible use of weak Ciphers/hashes"); ArrayList eA = new ArrayList<>(); for (String c : weakCrypto) eA.addAll(this.sU.searchForKeyInJava(c,this.codeRoot)); this.printE(eA); } /** * Use of cryptography */ private void crypto(){ OutBut.printH2("Crypto and hashing keywords"); String[] keys = {"aes", "crypt", "cipher", "sha1", "sha2","key" }; ArrayList eA = this.returnFNameLineGroup(keys, false); this.printE(this.removeDuplicatedSimpleEntries(eA)); } /** * Capture all strings in code and save the result to stringArrayList */ private void getStringsInCode(){ System.out.println("\n"); // OutBut.printH2("Strings"); ArrayList e = this.sU.searchForKeyInJava("\"",this.codeRoot); ArrayList eResult = this.sU.refineSearch(e, "[^:](\".+\")"); // ArrayList eResult = this.sU.refineSearch(e, "(\"\\w{32}\"|\"\\w{40}\"|\"\\w{56}\"|\"\\w{64}\")"); // this.printE(this.removeDuplicatedSimpleEntries(eResult)); this.stringArrayList = eResult; } private void getHashes(){ System.out.println("\n"); OutBut.printH2("Strings"); ArrayList e = this.sU.searchForKeyInJava("\"",this.codeRoot); ArrayList eResult = this.sU.refineSearch(e, "(\"\\w{32}\"|\"\\w{40}\"|\"\\w{56}\"|\"\\w{64}\")"); this.printE(this.removeDuplicatedSimpleEntries(eResult)); } ///////////////////////////////// Communication /////////////////////////////////// private void httpUrls(){ OutBut.printH2("URLs in code"); this.getHttpUris(); this.getPathes(); OutBut.printH2("IP addresses in code"); this.getIPAddresses(); } public void getHttpUris(){ ArrayList hits = this.sU.searchForKeyInJava("http",this.codeRoot); ArrayList eResult = this.sU.refineSearch(hits, "(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); this.printE(eResult); /* OutBut.printH3("HTTP URL summary:"); ArrayList uris = new ArrayList<>(); for (SimpleEntry e: hits){ uris.add(this.correctUrl((String)e.getValue())); // OutBut.printNormal(this.correctUrl((String)e.getValue())); } OtherUtil.printStringArray(OtherUtil.removeDuplicates(uris)); */ } /** * Searches for any possible paths: whether API paths or other */ private void getPathes() { // ArrayList hits = this.sU.searchForKeyInJava("\\(\\\"(.*\\/.*)\\\"\\)",this.codeRoot); ArrayList hits = this.sU.refineSearch(this.stringArrayList, "@\\w+\\(\\\"(.*\\/.*)+\\\"\\)"); if (!hits.isEmpty()) { OutBut.printH3("Possible paths:"); this.printE(hits); } } private void getIPAddresses() { String regex = "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\" + ".([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])"; ArrayList e = this.sU.searchForKeyInJava(".",this.codeRoot); ArrayList eResult = this.sU.refineSearch(e, regex); this.printE(this.removeDuplicatedSimpleEntries(eResult)); } private void findSchemes() { OutBut.printH2("Schemes and other URIs"); ArrayList hits = this.sU.searchForKeyInJava("://",this.codeRoot); this.printE(hits); } private void pinning(){ OutBut.printH2("Pinning"); ArrayList hits = this.sU.searchForKeyInJava("certificatePinner",this.codeRoot); this.printE(hits); } private String correctUrl(String line) { String url=""; Matcher m = Pattern.compile("http(s?)://(.+?)[\"'\\s]").matcher(line); if (m.find()) { url = line.substring(m.start(0),m.end(0)-1); } return url; } //////////////////////////// Disclosure //////////////////////////////////// /** * Search For all logcat commands In Code */ public void logInCode(){ OutBut.printH2("Logging messages in logcat"); ArrayList e = this.sU.searchForKeyInJava("Log",this.codeRoot) ; ArrayList eResult = this.sU.refineSearch(e, ".*Log\\.\\w\\((\\+?)"); eResult.addAll(this.sU.refineSearch(e, ".*getLogger\\(\\)\\.(\\w)")); this.printE(eResult); } private void testDisclosure(){ OutBut.printH2("Test"); ArrayList hits = this.sU.searchForKeyInJava("test",this.codeRoot); hits = this.sU.refineSearch(hits, "(.*(test|TEST).*)"); this.printE(hits); } /** * stuff like // and maybe what's between /* and *\/ * Regex for comment: // not proceeded by a : (not a URI), proceeded by any space then no digit (eliminate undecompiled code) */ public void commentsInCode(){ System.out.println("\n"); OutBut.printH2("Comments"); ArrayList eComments = new ArrayList<>(); ArrayList e = this.sU.searchForKeyInJava("//",this.codeRoot); ArrayList eResult = this.sU.refineSearch(e, "^\\s*//(\\s*[a-zA-Z]+)"); eResult= this.sU.refineSearch(eResult, "^(?!\\s*//\\s*from\\s*to\\s*target\\s*type)(.*$)"); eResult= this.sU.refineSearch(eResult, "^(?!\\s*//\\s*Byte\\s*code:)(.*$)"); eResult= this.sU.refineSearch(eResult, "^(?!\\s*//\\s*start\\s*length\\s*slot\\s*name\\s*signature)(.*$)"); eResult= this.sU.refineSearch(eResult, "^(?!\\s*//\\s*Local\\s*variable\\s*table)(.*$)"); eResult= this.sU.refineSearch(eResult, "^(?!\\s*//\\s*Exception\\s*table:)(.*$)"); eComments.addAll(eResult); ArrayList eM = this.sU.searchForKeyInJava("*",this.codeRoot); ArrayList eResultMLine = this.sU.refineSearchMatch(eM, "\\s*/\\*.+"); eResultMLine = this.sU.refineSearchMatch(eResultMLine, "^(?!\\s*/\\*\\s*Error\\s*\\*/)(.*$)"); // ArrayList eResultMLine2 = this.sU.refineSearchMatch(eM, "\\s*\\*.+"); eComments.addAll(eResultMLine); // eComments.addAll(eResultMLine2); this.printE(this.removeDuplicatedSimpleEntries(eComments)); } /** * Search for anything that can disclose credentials: * pass, password, username, userID, credentials */ public void credentialsInCode(){ System.out.println("\n"); OutBut.printH2("Possible credentials disclosure"); String[] keys = {"pass","password", "pwd", "username", "user name", "userID", "credential", "admin"}; ArrayList eA = this.returnFNameLineGroup(keys, false); this.printE(this.removeDuplicatedSimpleEntries(eA)); } //////////////////////////////////////Utils ///////////////////////////////// /** * Print squeeze output to stdout * @param eArray */ private void printE(ArrayList eArray){ for (SimpleEntry e: eArray){ String fileName=e.getKey().toString().replaceAll(this.codeRoot, "[Java_Code_Dir]"); System.out.println("#FileName: "+fileName); System.out.println(" "+e.getValue()+"\n"); } } private ArrayList removeDuplicatedSimpleEntries(ArrayList orig) { return new ArrayList(new LinkedHashSet(orig)); } /** * Get file names from a hits arraylist. Removes duplicates and sort * @param hits * @return */ private ArrayList returnFileNames(ArrayList hits){ ArrayList files = new ArrayList<>(); for (SimpleEntry e:hits) files.add(e.getKey().toString().replaceAll(this.codeRoot, "[Java_Code_Dir]")); ArrayList ret= new ArrayList(new LinkedHashSet(files)); return ret; } private ArrayList returnValues(ArrayList hits){ ArrayList files = new ArrayList<>(); for (SimpleEntry e:hits) files.add(e.getValue().toString()); Collections.sort(files); ArrayList ret= new ArrayList(new LinkedHashSet(files)); return ret; } /** * Searches for a group of Keys and print each occurrence and its file name. * @param keys */ private ArrayList returnFNameLineGroup(String[] keys, boolean printName){ ArrayList eA = new ArrayList<>(); ArrayList keyRes = new ArrayList<>(); for (String s: keys){ keyRes = this.sU.searchForKeyInJava(s,this.codeRoot); if (printName && (!keyRes.isEmpty())) OutBut.printH3(s); eA.addAll(this.sU.searchForKeyInJava(s,this.codeRoot)); } return eA; } /** * Searches for a type of files in APK and prints a list of these files */ private void searchForFilesInAPK(String[] extension){ List files = this.sU.search4FileInDir(TicklerVars.extractedDir, extension); if (!files.isEmpty()) { for (File f:files ){ OutBut.printNormal(f.getAbsolutePath().replaceAll(TicklerVars.extractedDir, "[Extracted_Apk]")); } OutBut.printNormal("\nWhere [Extracted_Apk] is "+TicklerVars.extractedDir); } } /** * Checks in a directory for the keywords of external storage */ public boolean isExternalStorage(){ ArrayList eArray = this.sU.searchForKeyInJava("getExternal",this.codeRoot); if (!this.sU.refineSearch(eArray, "(getExternalFilesDir)").isEmpty() || !this.sU.refineSearch(eArray, "getExternalStoragePublicDirectory").isEmpty()) return true; return false; } public void squeezeJson(String filePath) { JsonParser jp = new JsonParser(); ArrayList eA,summaryArray; SimpleEntry keyResult; String[] searchKeys; String json; try { json=this.readSqueezeJson(filePath); } catch (IOException e) { json=this.jsonSq; } summaryArray=new ArrayList(); ArrayList>> arr = jp.parseJsonString(json); for (SimpleEntry> e : arr) { OutBut.printH2(e.getKey()); Object[] aL = e.getValue().toArray(); searchKeys= Arrays.copyOf(aL, aL.length, String[].class);; //To get the number of hits of each key in searchKeys for (String key : searchKeys) { String[] keyArr = {key}; eA = this.returnFNameLineGroup(keyArr, false); keyResult=new SimpleEntry(key, eA.size()); summaryArray.add(keyResult); this.printE(this.removeDuplicatedSimpleEntries(eA)); } } //Print Summary OutBut.printH2("Results Summary"); for (SimpleEntry e : summaryArray) { OutBut.printNormal(e.getKey()+":\t"+e.getValue()); } // catch(IOException e) { // OutBut.printError("JSON File "+filePath+" does not exist"); // } } private String readSqueezeJson(String filePath) throws IOException { FileUtil fU = new FileUtil(); return fU.readFile(filePath); } /** * Write squeeze output in a squeeze file */ private void writeSqueezeInFile() { try { this.origSysOut = System.out; FileUtil fU = new FileUtil(); // fU.writeFile(TicklerVars.squeezeFile, "Tickler Squeeze"); PrintStream fileOut = new PrintStream(TicklerVars.squeezeFile); OutBut.printH1("Code Squeeze"); OutBut.printH2("Output is also saved at "+ TicklerVars.squeezeFile); System.setOut(fileOut); } catch(FileNotFoundException e) { e.printStackTrace(); } } private void printSqueezeFile() { FileUtil fU = new FileUtil(); try { String squeezeOutput = fU.readFile(TicklerVars.squeezeFile); OutBut.printNormal(squeezeOutput); } catch (IOException e) { OutBut.printError("Problem reading squeeze output file: "+TicklerVars.squeezeFile); } } /** * Write output back to system out */ private void backToSystemOut() { System.setOut(this.origSysOut); } } ================================================ FILE: src/main/java/commandExec/Commando.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package commandExec; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import base.FileUtil; import cliGui.OutBut; import initialization.TicklerVars; import java.io.File; import java.io.FileWriter; public class Commando { private StringBuffer output; //////////////////// Process Commands ////////////////////// /** * Execute a command as a process * @param command * @return ArrayList of 2 strings: * 1) The output of the command * 2) The error code of command execution */ public ArrayList executeProcessString(String command){ int errCode=999; ArrayList returnArr = new ArrayList(); List args = Arrays.asList(command.split(" ")); String output="!!! ERROR: command "+command+" failed to execute successfully :("; try{ ProcessBuilder build = new ProcessBuilder(args); Process p = build.start(); errCode = p.waitFor(); output= this.getProcessOp(p); } catch(Exception e){ e.printStackTrace(); } returnArr.add(output); returnArr.add(new Integer(errCode).toString()); return returnArr; } /** * Execute a command as a list of arguments and prints its output if "output" is true * @param args * @return return code, errCode 999 is a default value in case an exception occurs before errCode is set */ public int executeProcessListPrintOP(String command,boolean output){ return this.executeProcessListMain(command, output,false,false); } public int executeProcessListPrintOPError(String command){ return this.executeProcessListMain(command, true, true,false); } public int executeProcessForAdbPull(String command){ return this.executeProcessListMain(command, false, true, true); } /** * Main method for executing a command as a list * @param args * @param output boolean: if true then print output * @param error boolean: if true then print output / save output * @param file boolean: if true then save the output in a temporary file (used with adb pull) * @return */ private int executeProcessListMain(String command,boolean output,boolean error,boolean file){ List args = Arrays.asList(command.split(" ")); int errCode=999; String tempPullLog = TicklerVars.logDir+".pullLog.log"; FileUtil fU = new FileUtil(); try{ ProcessBuilder build = new ProcessBuilder(args); build.redirectErrorStream(error); Process p = build.start(); //Print output? if (output){ System.out.println(); OutBut.printH3("Command Output"); this.printProcessOp(p); } //Writes output in a file else if (file) { fU.createDirOnHost(TicklerVars.logDir); new File(tempPullLog).createNewFile(); this.saveProcessOp(p, tempPullLog); } errCode = p.waitFor(); } catch(Exception e){ e.printStackTrace(); } //If command is successful then delete temp log file of the command execution if (errCode == 0 && file){ fU.deleteFromHost(tempPullLog); } return errCode; } /** *Print stout of a command * @param process */ private void printProcessOp(Process process) { BufferedReader reader = new BufferedReader(new InputStreamReader( process.getInputStream())); String line = ""; try { while ((line = reader.readLine())!= null) { System.out.println(line); } } catch(Exception e){ e.printStackTrace(); } } /** * Used with adb pull command * @param process * @param fileName */ private void saveProcessOp(Process process, String fileName){ BufferedReader reader = new BufferedReader(new InputStreamReader( process.getInputStream())); String line = ""; try { BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); while ((line = reader.readLine())!= null) { writer.write(line+"\n"); } writer.close(); } catch(IOException e) { e.printStackTrace(); } } /** * Retruns the output of the command * @param process * @return */ private String getProcessOp(Process process){ BufferedReader reader = new BufferedReader(new InputStreamReader( process.getInputStream())); String line = ""; StringBuffer outputBuff = new StringBuffer(); try { while ((line = reader.readLine())!= null) { outputBuff.append(line + "\n"); } } catch(Exception e){ e.printStackTrace(); } return outputBuff.toString(); } //////////////// Runtime execution //////////////////// public String executeCommand(String command) { return this.executeCommand(command, true); } /** * Executes a command with an option not to wait. * @param command * @param wait * @return */ public String executeCommand(String command, boolean wait) { this.output = new StringBuffer( ); Process process; try { process=Runtime.getRuntime().exec(command); if (wait) process.waitFor(); BufferedReader reader = new BufferedReader(new InputStreamReader( process.getInputStream())); String line = ""; while ((line = reader.readLine())!= null) { output.append(line + "\n"); } if (output.toString().isEmpty()) { reader = new BufferedReader(new InputStreamReader( process.getErrorStream())); line = ""; while ((line = reader.readLine())!= null) { output.append(line + "\n"); } } } catch(Exception e) { e.printStackTrace(); } return output.toString(); } public String executePythonScript(String command) { // command = "frida -U wana.notenaf.intenttest -l /home/aabolhadid/tools/Frida/myScripts/get_values.js"; this.output = new StringBuffer( ); Process process; try { process=Runtime.getRuntime().exec(command); BufferedReader reader = new BufferedReader(new InputStreamReader( process.getErrorStream())); String line = ""; while ((line = reader.readLine())!= null) { output.append(line + "\n"); OutBut.printNormal(line); } if (output.toString().isEmpty()) { reader = new BufferedReader(new InputStreamReader( process.getErrorStream())); line = ""; while ((line = reader.readLine())!= null) { // output.append(line + "\n"); OutBut.printNormal(line); } } } catch(Exception e) { e.printStackTrace(); } return output.toString(); } //////////////////////////////////// execute ADB or Root Commands ////////////////////////// public String execADB(String command, boolean wait) { return this.executeCommand("adb shell "+command, wait); } public String execADB(String command) { return this.executeCommand("adb shell "+command); } public String execRoot(String command) { // return this.executeCommand("adb shell su -c "+command); String result = this.executeCommand("adb shell su -c "+command); if (result.contains("invalid uid")) { // DEvice / Emulator does not accept su -c, use su UID command result = this.executeCommand("adb shell su 0 "+command); } return result; } /** * Currently used to print OP of Start commands * @param command * @return */ public int execADBPrintOP(String command){ String fullCommand = "adb shell "+command; return this.executeProcessListPrintOP(fullCommand,true); } /** * Currently used to print OP of Start commands * @param command * @return */ public int execRootPrintOP(String command){ String fullCommand = "adb shell su -c "+command; int result=99; //Quick and Dirty solution to execute root commands on emulators, fixing invalid uid error String testCommand = this.executeCommand("adb shell su -c "); if (testCommand.contains("invalid uid")) { result = this.executeProcessListPrintOP("adb shell su 0 "+command,true); } else { result = this.executeProcessListPrintOP(fullCommand,true); } return result; } } ================================================ FILE: src/main/java/components/Action.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; public class Action { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "Action: name: "+this.getName()+"\n"; } } ================================================ FILE: src/main/java/components/Activity.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; import java.util.ArrayList; public class Activity implements IComponent,IActivityService{ String name, exp, permission; boolean isExported; ArrayList intents; public String getName() { return name; } public void setName(String name) { this.name = name; } /*public void setExp(String exp) { this.exp = exp; } public String getExp() { return exp; }*/ public boolean isExported() { return isExported; } public void setExported(boolean exported) { this.isExported = exported; } public ArrayList getIntent() { return intents; } public void setIntent(ArrayList intents) { this.intents = intents; } public String getPermission() { return permission; } public void setPermission(String permission) { this.permission = permission; } } ================================================ FILE: src/main/java/components/Application.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; import java.util.List; public class Application { boolean isAllowBackup,isDebuggable; List activites; List services; List receivers; List providers; String name; public Application() { this.isAllowBackup = false; } public boolean isAllowBackup() { return isAllowBackup; } public void setAllowBackup(boolean isAllowBackup) { this.isAllowBackup = isAllowBackup; } public List getActivites() { return activites; } public void setActivites(List activites) { this.activites = activites; } public List getServices() { return services; } public void setServices(List services) { this.services = services; } public List getReceivers() { return receivers; } public void setReceivers(List receivers) { this.receivers = receivers; } public List getProviders() { return providers; } public void setProviders(List providers) { this.providers = providers; } public boolean isDebuggable() { return isDebuggable; } public void setDebuggable(boolean isDebuggable) { this.isDebuggable = isDebuggable; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: src/main/java/components/Category.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; public class Category { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "Category: name "+this.getName()+"\n"; } } ================================================ FILE: src/main/java/components/DataUri.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; import java.util.HashMap; import java.util.Map; /** * TODO: assume multiple mime-types, pathPrefixes * @author aabolhadid * */ public class DataUri { private String scheme,host,port,path,pathPrefix,pathPattern,mimeType; private MapdataMap; public String getScheme() { return scheme; } public void setScheme(String scheme) { this.scheme = scheme; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public String getPort() { return port; } public void setPort(String port) { this.port = port; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public String getPathPrefix() { return pathPrefix; } public void setPathPrefix(String pathPrefix) { this.pathPrefix = pathPrefix; } public String getPathPattern() { return pathPattern; } public void setPathPattern(String pathPattern) { this.pathPattern = pathPattern; } public String getMimeType() { return mimeType; } public void setMimeType(String mimeType) { this.mimeType = mimeType; } public Map getDataMap(){ dataMap =new HashMap(); this.fillScheme(); this.fillRest("host", host); this.fillRest("path", path); this.fillRest("port", port); this.fillRest("pathPrefix", pathPrefix); this.fillRest("pathPattern", pathPattern); this.fillRest("mimeType", mimeType); return dataMap; } /** * Fill scheme in the map. * If empty then use content and file */ private void fillScheme(){ if (scheme !=null) dataMap.put("scheme", this.getScheme()); } private void fillRest(String key, String value){ if (value != null) dataMap.put(key,value); } } ================================================ FILE: src/main/java/components/IActivityService.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; import java.util.List; public interface IActivityService { public List getIntent(); public String getName(); } ================================================ FILE: src/main/java/components/IComponent.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; import java.util.ArrayList; public interface IComponent { public boolean isExported(); public void setExported(boolean isExported); public ArrayList getIntent(); public String getName(); public String getPermission(); public void setName(String name); public void setPermission(String permission); public void setIntent(ArrayList intentFilters); } ================================================ FILE: src/main/java/components/Intent.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; import java.util.List; public class Intent { List actions; List categories; List data; int priority; public List getAction() { return actions; } public void setAction(List action) { this.actions = action; } public List getCategory() { return categories; } public void setCategory(List category) { this.categories = category; } public List getData() { return data; } public void setData(List data) { this.data = data; } public int getPriority() { return priority; } public void setPriority(int priority) { this.priority = priority; } public String toString() { String toReturn=">>Intent: Data "+this.getData()+" Priority "+Integer.toString(this.getPriority())+"\n"; for (Action a : this.actions) { toReturn+=a.toString(); } for (Category c : this.categories) { toReturn+=c.toString(); } return toReturn; } } ================================================ FILE: src/main/java/components/Manifest.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; import java.util.ArrayList; public class Manifest { private ArrayList intents; private ArrayList usesPermissions; private ArrayList Permissions; private Application application; private String pkgName; public ArrayList getIntents() { return intents; } public void setIntents(ArrayList intents) { this.intents = intents; } public ArrayList getUsesPermissions() { return usesPermissions; } public void setUsesPermissions(ArrayList usesPermissions) { this.usesPermissions = usesPermissions; } public ArrayList getPermissions() { return Permissions; } public void setPermissions(ArrayList permissions) { Permissions = permissions; } public Application getApplication() { return application; } public void setApplication(Application application) { this.application = application; } public String getPkgName() { return pkgName; } public void setPkgName(String pkgName) { this.pkgName = pkgName; } } ================================================ FILE: src/main/java/components/Permission.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; public class Permission { String name,protectionLevel; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getProtectionLevel() { return protectionLevel; } public void setProtectionLevel(String protectionLevel) { this.protectionLevel = protectionLevel; } } ================================================ FILE: src/main/java/components/Provider.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; import java.util.ArrayList; public class Provider implements IComponent{ String name,permission,authorities; boolean isExported; ArrayList intent; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPermission() { return permission; } public void setPermission(String permission) { this.permission = permission; } public String getAuthorities() { return authorities; } public void setAuthorities(String authorities) { this.authorities = authorities; } public boolean isExported() { return isExported; } public void setExported(boolean isExported) { this.isExported = isExported; } public ArrayList getIntent() { return intent; } public void setIntent(ArrayList intent) { this.intent = intent; } public String getExp() { return null; } } ================================================ FILE: src/main/java/components/Receiver.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; import java.util.ArrayList; public class Receiver implements IComponent{ boolean isExported; String name,permission,exp; ArrayList intents; public boolean isExported() { return isExported; } public void setExported(boolean isExported) { this.isExported = isExported; } public String getExp() { return exp; } public void setExp(String exp) { this.exp = exp; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPermission() { return permission; } public void setPermission(String permission) { this.permission = permission; } public ArrayList getIntent() { return intents; } public void setIntent(ArrayList intents) { this.intents = intents; } } ================================================ FILE: src/main/java/components/Service.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; import java.util.ArrayList; import java.util.List; public class Service implements IComponent,IActivityService{ String name,exp, permission; boolean isExported; ArrayList intent; public Service() { this.isExported = false; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getExp() { return exp; } public void setExp(String exp) { this.exp = exp; } public boolean isExported() { return isExported; } public void setExported(boolean isExported) { this.isExported = isExported; } public ArrayList getIntent() { return intent; } public void setIntent(ArrayList intent) { this.intent = intent; } public String getPermission() { return permission; } public void setPermission(String permission) { this.permission = permission; } } ================================================ FILE: src/main/java/components/UsesPermission.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components; public class UsesPermission { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: src/main/java/components/old/Action.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; public class Action { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "Action: name: "+this.getName()+"\n"; } } ================================================ FILE: src/main/java/components/old/Activity.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import java.util.ArrayList; public class Activity implements IComponent,IActivityService{ String name, exp, permission; boolean isExported; ArrayList intents; public String getName() { return name; } public void setName(String name) { this.name = name; } /*public void setExp(String exp) { this.exp = exp; } public String getExp() { return exp; }*/ public boolean isExported() { return isExported; } public void setExported(boolean exported) { this.isExported = exported; } public ArrayList getIntent() { return intents; } public void setIntent(ArrayList intents) { this.intents = intents; } public String getPermission() { return permission; } public void setPermission(String permission) { this.permission = permission; } } ================================================ FILE: src/main/java/components/old/Application.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElements; public class Application { boolean isAllowBackup,isDebuggable; List activites; List services; List receivers; List providers; String name; public Application() { this.isAllowBackup = false; } public boolean isAllowBackup() { return isAllowBackup; } public void setAllowBackup(boolean isAllowBackup) { this.isAllowBackup = isAllowBackup; } public List getActivites() { return activites; } @XmlElements({ @XmlElement(name="activity"), @XmlElement(name="activity-alias") }) public void setActivites(List activites) { this.activites = activites; } public List getServices() { return services; } @XmlElement(name="service") public void setServices(List services) { this.services = services; } public List getReceivers() { return receivers; } @XmlElement(name="receiver") public void setReceivers(List receivers) { this.receivers = receivers; } public List getProviders() { return providers; } @XmlElement(name="provider") public void setProviders(List providers) { this.providers = providers; } public boolean isDebuggable() { return isDebuggable; } @XmlAttribute(name="debuggable",namespace="http://schemas.android.com/apk/res/android") public void setDebuggable(boolean isDebuggable) { this.isDebuggable = isDebuggable; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: src/main/java/components/old/Category.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import javax.xml.bind.annotation.XmlRootElement; public class Category { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "Category: name "+this.getName()+"\n"; } } ================================================ FILE: src/main/java/components/old/DataUri.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import java.util.HashMap; import java.util.Map; import javax.xml.bind.annotation.XmlAttribute; /** * TODO: assume multiple mime-types, pathPrefixes * @author aabolhadid * */ public class DataUri { private String scheme,host,port,path,pathPrefix,pathPattern,mimeType; private MapdataMap; public String getScheme() { return scheme; } @XmlAttribute(name="scheme",namespace="http://schemas.android.com/apk/res/android") public void setScheme(String scheme) { this.scheme = scheme; } @XmlAttribute(name="host",namespace="http://schemas.android.com/apk/res/android") public String getHost() { return host; } public void setHost(String host) { this.host = host; } @XmlAttribute(name="port",namespace="http://schemas.android.com/apk/res/android") public String getPort() { return port; } public void setPort(String port) { this.port = port; } @XmlAttribute(name="path",namespace="http://schemas.android.com/apk/res/android") public String getPath() { return path; } public void setPath(String path) { this.path = path; } @XmlAttribute(name="pathPrefix",namespace="http://schemas.android.com/apk/res/android") public String getPathPrefix() { return pathPrefix; } public void setPathPrefix(String pathPrefix) { this.pathPrefix = pathPrefix; } @XmlAttribute(name="pathPattern",namespace="http://schemas.android.com/apk/res/android") public String getPathPattern() { return pathPattern; } public void setPathPattern(String pathPattern) { this.pathPattern = pathPattern; } @XmlAttribute(name="mimeType",namespace="http://schemas.android.com/apk/res/android") public String getMimeType() { return mimeType; } public void setMimeType(String mimeType) { this.mimeType = mimeType; } public Map getDataMap(){ dataMap =new HashMap(); this.fillScheme(); this.fillRest("host", host); this.fillRest("path", path); this.fillRest("port", port); this.fillRest("pathPrefix", pathPrefix); this.fillRest("pathPattern", pathPattern); this.fillRest("mimeType", mimeType); return dataMap; } /** * Fill scheme in the map. * If empty then use content and file */ private void fillScheme(){ if (scheme !=null) dataMap.put("scheme", this.getScheme()); } private void fillRest(String key, String value){ if (value != null) dataMap.put(key,value); } } ================================================ FILE: src/main/java/components/old/IActivityService.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import java.util.List; public interface IActivityService { public List getIntent(); public String getName(); } ================================================ FILE: src/main/java/components/old/IComponent.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import java.util.ArrayList; public interface IComponent { public boolean isExported(); public void setExported(boolean isExported); public ArrayList getIntent(); public String getName(); public String getPermission(); public void setName(String name); public void setPermission(String permission); public void setIntent(ArrayList intentFilters); } ================================================ FILE: src/main/java/components/old/Intent.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; public class Intent { List actions; List categories; List data; int priority; public List getAction() { return actions; } @XmlElement public void setAction(List action) { this.actions = action; } public List getCategory() { return categories; } @XmlElement public void setCategory(List category) { this.categories = category; } public List getData() { return data; } @XmlElement public void setData(List data) { this.data = data; } public int getPriority() { return priority; } @XmlAttribute public void setPriority(int priority) { this.priority = priority; } public String toString() { String toReturn=">>Intent: Data "+this.getData()+" Priority "+Integer.toString(this.getPriority())+"\n"; for (Action a : this.actions) { toReturn+=a.toString(); } for (Category c : this.categories) { toReturn+=c.toString(); } return toReturn; } } ================================================ FILE: src/main/java/components/old/Manifest.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import java.util.ArrayList; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; public class Manifest { private ArrayList intents; private ArrayList usesPermissions; private ArrayList Permissions; private Application application; private String pkgName; public ArrayList getIntents() { return intents; } public void setIntents(ArrayList intents) { this.intents = intents; } public ArrayList getUsesPermissions() { return usesPermissions; } public void setUsesPermissions(ArrayList usesPermissions) { this.usesPermissions = usesPermissions; } public ArrayList getPermissions() { return Permissions; } public void setPermissions(ArrayList permissions) { Permissions = permissions; } public Application getApplication() { return application; } public void setApplication(Application application) { this.application = application; } public String getPkgName() { return pkgName; } public void setPkgName(String pkgName) { this.pkgName = pkgName; } } ================================================ FILE: src/main/java/components/old/Permission.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import javax.xml.bind.annotation.XmlAttribute; public class Permission { String name,protectionLevel; public String getName() { return name; } @XmlAttribute(name="name",namespace="http://schemas.android.com/apk/res/android") public void setName(String name) { this.name = name; } public String getProtectionLevel() { return protectionLevel; } @XmlAttribute(name="protectionLevel",namespace="http://schemas.android.com/apk/res/android") public void setProtectionLevel(String protectionLevel) { this.protectionLevel = protectionLevel; } } ================================================ FILE: src/main/java/components/old/Provider.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import java.util.ArrayList; import javax.xml.bind.annotation.XmlAttribute; public class Provider implements IComponent{ String name,permission,authorities; boolean isExported; ArrayList intent; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPermission() { return permission; } public void setPermission(String permission) { this.permission = permission; } public String getAuthorities() { return authorities; } public void setAuthorities(String authorities) { this.authorities = authorities; } public boolean isExported() { return isExported; } public void setExported(boolean isExported) { this.isExported = isExported; } public ArrayList getIntent() { return intent; } public void setIntent(ArrayList intent) { this.intent = intent; } public String getExp() { return null; } } ================================================ FILE: src/main/java/components/old/Receiver.java ================================================ /******************************************************************************* * Copyright 2019 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import java.util.ArrayList; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; public class Receiver implements IComponent{ boolean isExported; String name,permission,exp; ArrayList intents; public boolean isExported() { return isExported; } public void setExported(boolean isExported) { this.isExported = isExported; } public String getExp() { return exp; } public void setExp(String exp) { this.exp = exp; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPermission() { return permission; } public void setPermission(String permission) { this.permission = permission; } public ArrayList getIntent() { return intents; } public void setIntent(ArrayList intents) { this.intents = intents; } } ================================================ FILE: src/main/java/components/old/Service.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; public class Service implements IComponent,IActivityService{ String name,exp, permission; boolean isExported; ArrayList intent; public Service() { this.isExported = false; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getExp() { return exp; } public void setExp(String exp) { this.exp = exp; } public boolean isExported() { return isExported; } public void setExported(boolean isExported) { this.isExported = isExported; } public ArrayList getIntent() { return intent; } public void setIntent(ArrayList intent) { this.intent = intent; } public String getPermission() { return permission; } public void setPermission(String permission) { this.permission = permission; } } ================================================ FILE: src/main/java/components/old/UsesPermission.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package components.old; import javax.xml.bind.annotation.XmlAttribute; public class UsesPermission { private String name; public String getName() { return name; } @XmlAttribute(name="name",namespace="http://schemas.android.com/apk/res/android") public void setName(String name) { this.name = name; } } ================================================ FILE: src/main/java/components/old/XMLReader.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package base; import java.awt.List; import java.io.File; import java.util.ArrayList; import javax.xml.bind.JAXBContext; import javax.xml.bind.Unmarshaller; import javax.xml.bind.UnmarshallerHandler; import attacks.ActivityStarter; import attacks.Broadcaster; //import brut.androlib.ApktoolProperties; import components.Manifest; /** * Marshal and Unmarshal Manifest file * Unused since v2.3 * @author aabolhadid * */ public class XMLReader{ private String manifestFile; private Manifest manifest; public XMLReader(String manifestFile) { this.manifestFile = manifestFile; this.unmarshalManifest(); } public void unmarshalManifest() { Manifest man=new Manifest(); File manifest = new File(this.manifestFile); try { JAXBContext context = JAXBContext.newInstance(Manifest.class); Unmarshaller jaxbUnmarshaller = context.createUnmarshaller(); man = (Manifest) jaxbUnmarshaller.unmarshal(manifest); } catch(Exception e) { System.out.println("ERROR: Manifest cannot be parsed"); e.printStackTrace(); } this.manifest = man; } public Manifest getManifest() { return this.manifest; } } ================================================ FILE: src/main/java/db/DatabaseTester.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package db; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import base.FileUtil; import base.OtherUtil; import base.SearchUtil; import cliGui.OutBut; import commandExec.Commando; import initialization.TicklerVars; public class DatabaseTester { private Commando commando; private FileUtil fileTrans; public DatabaseTester() { this.commando = new Commando(); this.fileTrans = new FileUtil(); } public ArrayList fetchAllDBs(String dir){ SearchUtil searcher = new SearchUtil(); List files = searcher.search4FileInDir(dir, null); ArrayList dbs = new ArrayList(); for (File f:files){ if (this.isFileDB(f)) dbs.add(f); } return dbs; } public void dbOption(String param){ if (param == null ||param.equals("e") || param.equals("encryption")){ OutBut.printH1("Database encryption"); this.testDBAllFiles(TicklerVars.dataDir); } else if (param.equals("l") || param.equals("list")){ this.listDatabases(TicklerVars.dataDir); } else if (param.equals("d") || param.equals("dump")){ this.chooseDBToDump(TicklerVars.dataDir); } else { OutBut.printError("Unknown option "+param); } } /** * Checks if the file is a database file * @param f File file under test * @return true: if file is DB */ public boolean isFileDB(File f){ String path = f.getAbsolutePath(); String command = "file "+ path+""; String result = commando.executeProcessString(command).get(0); if (result.matches("(?s).*\\bdatabase\\b.*")){ return true; } return false; } /** * Lists all databases in a specific directory * @param dir */ public ArrayList listDatabases(String dir){ ArrayList allDB = this.fetchAllDBs(dir); OutBut.printH1("List of Databases of the app"); System.out.println("!!!! WARNING: Encrypted databases might not be detected !!!!\n\n"); for (int i=0;i1){ System.out.println("\n...Where [Tickler_App_Dir] is "+TicklerVars.appTickDir); } return allDB; } //////////////////////////// DB Encryption /////////////////////////// /** * Tests the encryption of all databases in a specific directory * @param dir */ public void testDBAllFiles(String dir) { ArrayList dbs= this.fetchAllDBs(dir); for (File f:dbs){ this.testDBEncryption(f); } System.out.println("!!! NOTE: Any path that contains a space is replaced by two underscores in the DataDir folder" + " on your machine"); } /** * Runs the encryption Tests on a single DB * @param f */ public void testDBEncryption(File f){ String result=""; if (this.isDBEncrypted(TicklerVars.replaceSpace(f.getAbsolutePath())) ) result="encrypted"; else result = "NOT encrypted"; System.out.println("Database:"+f.getName()+" is "+result); } /** * Tests if a single DB is encrypted * @param f */ public boolean isDBEncrypted(String dbName) { String command = "sqlite3 "+dbName+" .tables"; String output = this.commando.executeCommand(command); if (output.contains("encrypted or is not a database")) return true; return false; } ///////////////////// Database Dump ///////////////////////////// /** * Lists databases to user and dumps one of them based on his choice * @param dir */ public void chooseDBToDump(String dir){ ArrayList allDB = this.listDatabases(dir); System.out.println("\nEnter the number of the database to be dumped...."); String choice = OtherUtil.pressAnyKeySilent(); Integer i=999; try { i = new Integer(choice); } catch(Exception e){ e.printStackTrace(); } if((i-1)< allDB.size() && i>0){ String dbFile = allDB.get((i-1)).getAbsolutePath(); String dbName = allDB.get((i-1)).getName(); String ts =this.fileTrans.prepareTimestampTransfer(); String dumpName=TicklerVars.transferDir+dbName+"_"+ts+".dump"; this.dumpDBToFile(dbFile, dumpName); if (this.fileTrans.isExist(dumpName)){ System.out.println("Database is dumped to file: "+dumpName); } } else { System.out.println("Incorrect choice, please enter a number from the database list"); } } /** * Dumps the whole database * @param dbName String: path of the database file * @return */ public String dumpDB(String dbName){ String command = "sqlite3 "+dbName+" .dump"; String output = this.commando.executeCommand(command,false); return output; } /** * Dumps the whole database into a text file * @param dbName String: path of the database file * @return dumpName ; path of the dump file */ public String dumpDBToFile(String dbName,String dumpFile){ FileUtil ft = new FileUtil(); if (dumpFile == null) dumpFile= dbName+".dump"; String output = this.dumpDB(dbName); ft.writeFile(dumpFile, output); return dumpFile; } ///////////////////////////// Search in DB ///////////////////////////// /** * Search for a key in all databases in Data Dir * @param key */ public void searchForKeyInDb(String key){ ArrayList dbs = this.fetchAllDBs(TicklerVars.dataDir); boolean found = false; for(File db: dbs){ if (this.searchForKeyInSingleDB(key, db)) found = true; } if(found){ OutBut.printNormal(""); OutBut.printStep("Where [Data_Dir] is "+TicklerVars.dataDir); } } /** * Search for a key in a single database file * @param key * @param db */ private boolean searchForKeyInSingleDB(String key, File db){ String dump, dbName, output; boolean found = false; ArrayList tableNames,noDupTabNames; dump = this.dumpDB(db.getAbsolutePath()); if (dump.contains(key)) { found = true; dbName= db.getAbsolutePath().replaceAll(TicklerVars.dataDir, "[Data_Dir]/"); tableNames = this.getKeyInfoFromDBDump(key, dump); noDupTabNames = OtherUtil.removeDuplicates(tableNames); output = "Database Name: "+dbName+"\t\tTable Name(s): "; for(String tabName : noDupTabNames){ output+=tabName+", "; } System.out.println(output.substring(0, output.length()-2)); } return found; } /** * Searches for a key in the dump of a database * @param key * @param dump * @return */ private ArrayList getKeyInfoFromDBDump(String key, String dump){ ArrayList tables = new ArrayList<>(); List lines= Arrays.asList(dump.split("\n")); for (String line: lines){ if (line.contains(key)){ tables.addAll(OtherUtil.getRegexFromString(line, "INSERT INTO \"(.*?)\"")); } } return tables; } } ================================================ FILE: src/main/java/device/Packagez.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package device; import java.util.ArrayList; import cliGui.OutBut; import commandExec.Commando; import initialization.TicklerVars; public class Packagez { public boolean searchPackage(String key,boolean syso){ boolean isFound=false; ArrayList results = new ArrayList<>(); String output = this.fetchInstalledPkgs(); String[] opLines = output.split("\n"); for (String s:opLines) if (s.toLowerCase().contains(key.toLowerCase())){ isFound = true; results.add(s); } if (!results.isEmpty() && syso){ OutBut.printH2("Possible Package names"); for (String s:results){ String res = s.substring(s.indexOf(":")+1); System.out.println(res); } } else if (syso) System.out.println("No package matches the search key :("); return isFound; } /** * Makes sure that the exact pkgName exists, Case sensitive and full match * @param pkgName */ public boolean isPackageExist(String pkgName){ String[] packages = this.fetchInstalledPkgs().split("\n"); for (String pkg:packages){ String pkg2 = pkg.replace("package:", ""); if (pkgName.equals(pkg2)) return true; } return false; } public String fetchInstalledPkgs(){ String command = "pm list package "; Commando commando = new Commando(); String output = commando.execADB(command); return output; } public void printInstalledPkgs() { String pkgs = this.fetchInstalledPkgs(); OutBut.printH1("Installed Packages"); System.out.println(pkgs); } /** * execute pm dump command to get a package's information * @return */ public ArrayList dumpInfo(){ ArrayList matches = new ArrayList<>(); String command = "pm dump "+TicklerVars.pkgName; Commando commando = new Commando(); String dump = commando.execADB(command, false); String userID = this.getParameterFromDump("userId", dump); String codePath = this.getParameterFromDump("codePath", dump); String versionName = this.getParameterFromDump("versionName", dump); String firstInstallTime = this.getParameterFromDump("firstInstallTime", dump); matches.add("userID: "+userID); matches.add("API path: "+codePath); matches.add("Version name: "+versionName); matches.add("Installation date: "+firstInstallTime); return matches; } public String getParameterFromDump(String par, String dump) { String value=""; int index = dump.indexOf(par); value = dump.substring(dump.indexOf("=", index)+1, dump.indexOf("\n", index)); return value; } } ================================================ FILE: src/main/java/docs/helpMsg.txt ================================================ usage: Tickler ============== Please make sure that the configuration file: Tickler.conf is at the same directory as the Tickler .jar file. Tickler.conf file defines Tickler's directory on the host and the temp directory on the android device. Options --------- -h, --help Prints this message -version Print version -screen Take a screenshot of the device -findPkg Search for a package name -pkgs List all installed packages on the android device -offline Offline Mode, no devices needed to be connected -pkg Specify package name of the app Options of pkg: -------------- -info List information about the app -squeeze All strings, log functions, possible credentials in decompiled APK -squeeze short Limits the squeezed code to the developer's code (most libraries excluded) -dbg, --debuggable Create a debuggable version of the app -apk Compiles a new apk from directory under the name of -dataDir [name] Copies data directory of app to Tickler Directory (DataDir or transfers/name) -diff Copies app's data directory before and after a user's action, then diffs between them -diff [d| detailed] like diff, but also shows the changes in case a text file or an unecrypted database is changed -db [option] [-nu] Database checks. By default app's Data Dir is copied to host before checks [-nu | --noUpdate] Does not update DataDir on host before checks [option] can be: [ |e|encryption] Checks whether the database files of the app are encrypted or not [l |list] Lists Databases of the app [d |dump] Dumps an unencrypted database -sc Search for "key" in the app's decompiled Java code -sd [-nu] Pulls app's Data directory and searches for "key" in it [-nu | --noUpdate] Does not pull DataDir on host before search -t [target] [-log] Tickels a target. The type of the attack depends on the target -log Captures logcat messages -l,--list [target] [-v] List components of type target -v List component(s) in details Targets: -------- -act,--activities Activities -ser,--services Services -prov,--providers Content providers -rec,--receivers Broadcast receivers [ | -comp] A specific component, its name has to be exactly as in Manifest file (also displayed by -l) -exp Applies [trigger | list] action to exported components only [ | -all] All components (default if none of the above targets are specified) Options that work with and without -pkg option ----------------------------------------------- -screen Captures a screenshot of the device -cp2host [destName] Copies any file/directory to the tickler's app directoy on the host -bg,--bgSnapshots Copies background screenshots that are saved on the device Frida ----- -frida enum Enumerates loaded classes -frida vals [-reuse] Displays arguments and return value of this method (only primitive datatypes and String) -frida set [-reuse] Sets the argument number to (only primitive datatypes and String) If > : sets the return value -frida script Run custom frida JS script Examples: --------- 1) List all components of package com.test.package, with detailed information ----------------------------------------------------------------------------- java -jar Tickler.jar -pkg com.test.package -l -v 2) Trigger exported activities of package com.test.package ----------------------------------------------------------- java -jar Tickler.jar -pkg com.test.package -t -act -exp 3) Squeeze app's decompiled Java code for strings, log messages...etc ----------------------------------------------------------------------- java -jar Tickler.jar -pkg com.test.package -squeeze - output is automatically saved in squeeze.txt file in the app's directory ================================================ FILE: src/main/java/exceptions/TNotFoundEx.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package exceptions; public class TNotFoundEx extends Exception { public TNotFoundEx(String message){ super(message); } } ================================================ FILE: src/main/java/frida/FridaBase.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package frida; import java.util.ArrayList; import apk.AppBroker; import cliGui.OutBut; import commandExec.Commando; import initialization.TicklerVars; public class FridaBase { public FridaBase() { } public void fridaEnumerateClasses(boolean reuse){ FridaEnumerateClasses enumClasses = new FridaEnumerateClasses(reuse); enumClasses.run(); } public void fridaScript(ArrayList args){ // FridaPythonScript script = new FridaPythonScript(args); // script.execute(); FridaJsScript script = new FridaJsScript(args.get(1)); script.prepareCommandNoSpawning(); OutBut.printNormal("\nPlease start the app before running this command\n"); script.run(); } public void fridaGetInputAndOutput(ArrayList args, boolean reuse){ FridaGetArgsAndReturn action = new FridaGetArgsAndReturn(reuse); action.run(args); } public void fridaSetValue(ArrayList args, boolean reuse){ FridaSetValue action = new FridaSetValue(reuse); action.run(args); } public void fridaUnpin(ArrayList args, boolean reuse) { FridaUnpinSslContext action = new FridaUnpinSslContext(reuse); action.run(args); } } ================================================ FILE: src/main/java/frida/FridaCli.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package frida; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import cliGui.OutBut; public class FridaCli { private FridaBase base; private FridaInit init; public FridaCli(){ this.base = new FridaBase(); this.init = new FridaInit(); this.init.initFrida(); } /** * arg0: Name of function * arg1- : args of the function * @param args */ public void fridaThis(String [] args, boolean reuse){ String functionName = args[0]; ArrayList scriptArgs = new ArrayList<>(Arrays.asList(args)); switch(functionName){ case "enum": this.base.fridaEnumerateClasses(reuse); break; case "script": this.base.fridaScript(scriptArgs); // case "vals": this.base.fridaGetInputAndOutput(scriptArgs, reuse); break; case "set": this.base.fridaSetValue(scriptArgs, reuse); break; case "unpin": this.base.fridaUnpin(scriptArgs, reuse); break; default: OutBut.printError("Unknown option "+functionName); break; } } } ================================================ FILE: src/main/java/frida/FridaEnumerateClasses.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package frida; import java.util.ArrayList; import cliGui.OutBut; import initialization.TicklerVars; public class FridaEnumerateClasses extends FridaJsAction{ public FridaEnumerateClasses(boolean reuseScript) { this.script = new FridaJsScript(FridaVars.ENUM_LOC); if (reuseScript) this.code = this.script.getCodeFromScript(); else this.code = FridaVars.ENUM_CODE; } public void run(){ OutBut.printNormal("\nPlease start the app before running this command\n"); this.executeNoSpawn(this.code); } } ================================================ FILE: src/main/java/frida/FridaGetArgsAndReturn.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package frida; import java.util.ArrayList; import base.FileUtil; import commandExec.Commando; import initialization.TicklerVars; /** * GEt input and output of a method, provided the following: * 1) the method is not overloaded: only one method of this name * 2) inputs and outputs are of primitive data types or String * @author aabolhadid * */ public class FridaGetArgsAndReturn extends FridaJsAction{ public FridaGetArgsAndReturn(boolean reuseScript) { this.script = new FridaJsScript(FridaVars.GET_VALS_JS); if (reuseScript) this.code = this.script.getCodeFromScript(); else this.code = FridaVars.GET_VALS_CODE; } public void run(ArrayList args){ String finalCode = this.prepareCode(args); this.execute(finalCode); } private String prepareCode(ArrayList args){ String tempCode = this.code.replaceAll("\\$className", args.get(1)).replaceAll("\\$method_name", args.get(2)); int numberOfArgs = new Integer(args.get(3)); ArrayList methodArgs = this.getMethodArguments(numberOfArgs); String methodArguments=""; String consoleLogArgs = ""; for (String s: methodArgs){ methodArguments+=s+", "; consoleLogArgs+="console.log(\"Input: \"+"+s+");\n"; } if (!methodArguments.isEmpty()) methodArguments = methodArguments.substring(0, methodArguments.length()-2); tempCode = tempCode.replaceAll("\\$args", methodArguments); tempCode = tempCode.replaceAll("\\$console_log_inputs", consoleLogArgs); return tempCode; } } ================================================ FILE: src/main/java/frida/FridaInit.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package frida; import base.FileUtil; import cliGui.OutBut; import commandExec.Commando; import initialization.TicklerVars; public class FridaInit { private Commando commando; private FileUtil fU; public FridaInit() { this.commando = new Commando(); this.fU = new FileUtil(); } public void initFrida(){ if (!(this.isFrida() && this.isFridaServer() && this.isPython())){ OutBut.printError("Frida is not properly configured"); System.exit(127); } this.fU.createDirOnHost(TicklerVars.fridaScriptsDir); } /** * Frida exists on host * @return */ private boolean isFrida(){ String cmd = "frida -h"; int ret = commando.executeProcessListPrintOP(cmd,false); if (ret != 0) return false; return true; } /** * If Frida server exists on the device * @return */ private boolean isFridaServer(){ String cmd = TicklerVars.fridaServerLoc+" -h"; String ret = commando.execADB(cmd); if (ret.toLowerCase().contains("--version")) return true; return false; } private boolean isPython(){ String cmd = "python3 -h"; int ret = commando.executeProcessListPrintOP(cmd,false); if (ret != 0) return false; return true; } public void startFridaServer(){ String cmd = TicklerVars.fridaServerLoc+"&"; commando.execRoot(cmd); } public void stopFridaServer(){ String[] serverName = TicklerVars.fridaServerLoc.split("/"); String serverPSOutput = commando.execRoot("ps | grep "+serverName[serverName.length -1]); if (!serverPSOutput.isEmpty()){ String serverPs = serverPSOutput.split("\\s+")[1]; commando.execRoot("kill -9 "+serverPs); try { Thread.sleep(5000); } catch(InterruptedException e){ e.printStackTrace(); } } } private void restartFridaServer() { this.stopFridaServer(); this.startFridaServer(); } } ================================================ FILE: src/main/java/frida/FridaJsAction.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package frida; import java.util.ArrayList; import commandExec.Commando; import initialization.TicklerChecks; import initialization.TicklerVars; public class FridaJsAction { protected FridaJsScript script; protected String code; public void execute(String finalCode) { TicklerChecks ticklerChecks = new TicklerChecks(); if(ticklerChecks.isEmulator() || this.isAppRunning()) this.executeNoSpawn(finalCode); else this.executeSpawn(finalCode); } public void executeSpawn(String finalCode){ this.script.writeCodeInScript(finalCode); this.script.prepareCommand(); this.script.run(); } public void executeNoSpawn(String finalCode){ this.script.writeCodeInScript(finalCode); this.script.prepareCommandNoSpawning(); this.script.run(); } protected ArrayList getMethodArguments(int num){ ArrayList methodArgs = new ArrayList<>(); for (int i=0;i Don't spawn !! */ public void prepareCommand(){ TicklerChecks ticklerChecks = new TicklerChecks(); if(ticklerChecks.isEmulator() || this.isAppRunning()) this.prepareCommandNoSpawning(); else this.prepareCommandSpawn(); } public void prepareCommandSpawn(){ this.command = "frida -U -f "+TicklerVars.pkgName+" -l "+this.scriptPath+" --no-pause" ; } public void prepareCommandNoSpawning(){ this.command = "frida -U "+TicklerVars.pkgName+" -l "+this.scriptPath ; } public void writeCodeInScript(String code){ this.fU = new FileUtil(); this.fU.writeFile(this.scriptPath, code); } public String getCodeFromScript(){ String code =""; try { code = this.fU.readFile(this.scriptPath); } catch(IOException e){ e.printStackTrace(); } return code; } public ArrayList getMethodArguments(int num){ ArrayList methodArgs = new ArrayList<>(); for (int i=0;i args; public FridaPythonScript(){ this.commando = new Commando(); } public FridaPythonScript(ArrayListargs){ this(); this.prepareArgs(args); } private void prepareArgs(ArrayListargs){ String origPath = args.get(1); String scriptPath = OtherUtil.getAbsolutePath(origPath); if (scriptPath == null){ OutBut.printError("Path of the script: "+origPath+" does not exist"); System.exit(127); } else{ this.setPath(scriptPath); // this.args = new ArrayList)args.subList(1, args.size()); this.args = new ArrayList<>( args.subList(2, args.size())); this.args.add(0, TicklerVars.pkgName); } } public void execute(){ String command = "python3 "+this.path; if (this.args!= null){ for (String s:this.args) command=command+" "+s; } commando.executePythonScript(command); } public void execute(ArrayList args){ this.args = args; this.execute(); } public ArrayList executeReturnOutput(ArrayList args){ String command = "python3 "+this.path; if (args!= null){ for (String s:args) command=command+" "+s; } commando.executeProcessListPrintOP(command, true); return new ArrayList(); } public String getOutput(ArrayList args){ return this.executeReturnOutput(args).get(1); } public void setPath(String path){ String scriptPath = OtherUtil.getAbsolutePath(path); if (scriptPath == null){ OutBut.printError("Path of the script: "+path+" does not exist"); System.exit(127); } else this.path = scriptPath; } } ================================================ FILE: src/main/java/frida/FridaScript.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package frida; import java.util.ArrayList; import commandExec.Commando; public class FridaScript { public String path; private Commando commando; public FridaScript(){ this.commando = new Commando(); } public ArrayList executeScript(ArrayList args){ String command = "python3 "+this.path; if (args!= null){ for (String s:args) command=command+" "+s; } ArrayList op =commando.executeProcessString(command); return op; } public String getOutput(ArrayList args){ return this.executeScript(args).get(1); } } ================================================ FILE: src/main/java/frida/FridaSetValue.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package frida; import java.util.ArrayList; import base.FileUtil; import cliGui.OutBut; import commandExec.Commando; import initialization.TicklerVars; /** * GEt input and output of a method, provided the following: * 1) the method is not overloaded: only one method of this name * 2) inputs and outputs are of primitive data types or String * @author aabolhadid * */ public class FridaSetValue { private FridaJsScript script; private String code; public FridaSetValue(boolean reuseScript) { this.script = new FridaJsScript(FridaVars.SET_VALS_JS); if (reuseScript) this.code = this.script.getCodeFromScript(); else this.code = FridaVars.SET_VALS_CODE; } public void run(ArrayList args){ String finalCode = this.prepareCode(args); this.script.writeCodeInScript(finalCode); this.script.prepareCommand(); this.script.run(); } // args would be like: set, ClassName, MethodName, number_of_args, valNum, newValue // myArgs : pkgName, ClassName, MethodName, methodArgs(arg0,1...etc) private String prepareCode(ArrayList args){ String tempCode = this.code.replaceAll("\\$className", args.get(1)).replaceAll("\\$methodName", args.get(2)); int numberOfArgs = new Integer(args.get(3)); ArrayList methodArgs = this.getMethodArguments(numberOfArgs); String methodArguments=""; String consoleLog = ""; for (String s: methodArgs){ methodArguments+=s+", "; } if (!methodArguments.isEmpty()) methodArguments = methodArguments.substring(0, methodArguments.length()-2); tempCode = tempCode.replaceAll("\\$args", methodArguments); String newValue = this.correctStringsInArgs(args.get(5)); if (this.isSetReturnValue(args.get(4))) { //Modify return value tempCode = tempCode.replaceAll("\\$returnValue", newValue); tempCode = tempCode.replaceAll("\\$output_line", "console.log(\"Old return value: \"+orig_return.toString()+ \". New return value: \"+"+newValue+");"); } else { int numberOfTarget = new Integer(args.get(4)); //Modify an argument String newArgs = this.getNewArgs(numberOfArgs,numberOfTarget,newValue); tempCode = tempCode.replaceAll("\\$returnValue", "this."+args.get(2)+newArgs); String y ="console.log(\"Arg number "+ numberOfTarget+": old value: \"+"+"arg"+numberOfTarget+ "+\". New value: \"+"+newValue+");"; tempCode = tempCode.replaceAll("\\$output_line", y ); } tempCode = tempCode.replaceAll("\\$output_line", consoleLog); return tempCode; } private ArrayList getMethodArguments(int num){ ArrayList methodArgs = new ArrayList<>(); for (int i=0;i args){ String finalCode = this.prepareCode(args); this.executeNoSpawn(finalCode); } private String prepareCode(ArrayList args) { Commando commando = new Commando(); String certLoc = args.get(1); String certName= this.fU.getFileNameFromPath(certLoc); String certOnDevice="/data/local/tmp/"+certName; this.fU.copyToDevice(certLoc, certOnDevice); String finalCode = this.code.replaceAll("\\$mitmCert", certOnDevice); commando.execRoot("chmod 666 "+certOnDevice); return finalCode; } } ================================================ FILE: src/main/java/frida/FridaVars.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package frida; import initialization.TicklerVars; public abstract class FridaVars { //script locations public static String ENUM_LOC = TicklerVars.fridaScriptsDir+"enumerate_classes.js"; public static String GET_VALS_JS = TicklerVars.fridaScriptsDir+"get_attributes_output.js"; public static String SET_VALS_JS = TicklerVars.fridaScriptsDir+"set_value_output.js"; public static String SSL_CONTEXT_UNPIN_JS = TicklerVars.fridaScriptsDir+"unpin_ssl_context.js"; /// Frida JS Scripts code public static String GET_VALS_CODE= "setTimeout(function(){ \n" + " Java.perform(function () { \n" + " var className = Java.use(\"$className\"); \n" + " className.$method_name.implementation = function ($args) { \n" + " console.log(\"$method_name\"+\" method started\"); \n" + " var returnValue = this.$method_name($args); \n" + " $console_log_inputs \n" + // " " + " if (returnValue != null ) {console.log(\"Output: \"+returnValue.toString()+\"\\n\");}\n" + " return returnValue; \n" + // " " + " }; \n" + // " " + " }); \n" + // " " + " },0); \n"; public static String SET_VALS_CODE = " setTimeout(function(){ \n " + " Java.perform(function () { \n " + " var className = Java.use(\"$className\"); \n " + " className.$methodName.implementation = function ($args) { \n " + " console.log(\"$methodName\"+\" method started\"); \n" + " var orig_return = this.$methodName($args); \n " + " $output_line \n " + " return $returnValue; \n " + " }; \n " + " }); \n " + " },0); \n "; public static String sslContextUnpin = " //Originally written by Piergiovanni Cipolloni : https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida/ \n " + " Java.perform(function (){ \n " + " console.log(\"[.] Cert Pinning Bypass/Re-Pinning\"); \n " + " var CertificateFactory = Java.use(\"java.security.cert.CertificateFactory\"); \n " + " var FileInputStream = Java.use(\"java.io.FileInputStream\"); \n " + " var BufferedInputStream = Java.use(\"java.io.BufferedInputStream\"); \n " + " var X509Certificate = Java.use(\"java.security.cert.X509Certificate\"); \n " + " var KeyStore = Java.use(\"java.security.KeyStore\"); \n " + " var TrustManagerFactory = Java.use(\"javax.net.ssl.TrustManagerFactory\"); \n " + " var SSLContext = Java.use(\"javax.net.ssl.SSLContext\"); \n " + " // Load CAs from an InputStream \n " + " console.log(\"[+] Loading our CA...\") \n " + " cf = CertificateFactory.getInstance(\"X.509\"); \n " + " try { \n " + " var fileInputStream = FileInputStream.$new(\"$mitmCert\"); \n " + " } \n " + " catch(err) { \n " + " console.log(\"[o] \" + err); \n " + " } \n " + " var bufferedInputStream = BufferedInputStream.$new(fileInputStream); \n " + " var ca = cf.generateCertificate(bufferedInputStream); \n " + " bufferedInputStream.close(); \n " + " var certInfo = Java.cast(ca, X509Certificate); \n " + " console.log(\"[o] Our CA Info: \" + certInfo.getSubjectDN()); \n " + " // Create a KeyStore containing our trusted CAs \n " + " console.log(\"[+] Creating a KeyStore for our CA...\"); \n " + " var keyStoreType = KeyStore.getDefaultType(); \n " + " var keyStore = KeyStore.getInstance(keyStoreType); \n " + " keyStore.load(null, null); \n " + " keyStore.setCertificateEntry(\"ca\", ca); \n " + " // Create a TrustManager that trusts the CAs in our KeyStore \n " + " console.log(\"[+] Creating a TrustManager that trusts the CA in our KeyStore...\"); \n " + " var tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); \n " + " var tmf = TrustManagerFactory.getInstance(tmfAlgorithm); \n " + " tmf.init(keyStore); \n " + " console.log(\"[+] Our TrustManager is ready...\"); \n " + " console.log(\"[+] Hijacking SSLContext methods now...\") \n " + " console.log(\"[-] Waiting for the app to invoke SSLContext.init()...\") \n " + " SSLContext.init.overload(\"[Ljavax.net.ssl.KeyManager;\", \"[Ljavax.net.ssl.TrustManager;\", \"java.security.SecureRandom\").implementation = function(a,b,c) { \n " + " console.log(\"[o] App invoked javax.net.ssl.SSLContext.init...\"); \n " + " SSLContext.init.overload(\"[Ljavax.net.ssl.KeyManager;\", \"[Ljavax.net.ssl.TrustManager;\", \"java.security.SecureRandom\").call(this, a, tmf.getTrustManagers(), c); \n " + " console.log(\"[+] SSLContext initialized with our custom TrustManager!\"); \n " + " } \n " + " }); \n " ; public static String ENUM_CODE="Java.perform(\n" +" function(){\n" +" Java.enumerateLoadedClasses(\n" +" {\n" +" \"onMatch\": function(className){\n" +" console.log(className) \n" +" },\n" +" \"onComplete\":function(){}\n" +" }\n" +" )\n" +"})\n"; } ================================================ FILE: src/main/java/info/InfoGathering.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package info; import java.util.List; import java.io.File; import java.util.AbstractMap.SimpleEntry; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.LinkedHashSet; import base.Base64Util; import base.FileUtil; import base.SearchUtil; import cliGui.OutBut; import code.JavaSqueezer; //import code.SmaliAnalyzer; import commandExec.Commando; import db.DatabaseTester; import initialization.TicklerVars; public class InfoGathering { private SearchUtil searcher; private String resXml; public InfoGathering(){ this.searcher = new SearchUtil(); this.resXml = "res/xml/"; } //////////////////// SD card ///////////////////// public String getSdcardDirectory(){ return this.searcher.searchOnDevice("/sdcard/Android", TicklerVars.pkgName); } /** * Searches in Java and Smali code if the app uses external storage * @return */ public boolean isExternalStorage(){ JavaSqueezer jCA = new JavaSqueezer(); boolean result = jCA.isExternalStorage(); if (!result) result = this.isExternalStorageDirectory(TicklerVars.smaliDir); return result; } /** * Checks in a directory for the keywords of external storage */ private boolean isExternalStorageDirectory(String loc){ ArrayList foundArray = searcher.search4KeyInDir(loc, "getExternal"); for (String s:foundArray) { if (s.contains("getExternalFilesDir") || s.contains("getExternalStoragePublicDirectory")){ return true; } } return false; } //////////////////////////// Interesting things in APK //////////////////////////// public void getCertificatesInApkDirectory(){ String[] certExtensions = {".cer",".crt",".der",".csr",".pfx",".p12",".pem",".p7b", ".p7r", ".spc"}; List result = this.searcher.search4FileInDir(TicklerVars.extractedDir, certExtensions ); boolean found = false; for (File f: result){ found = true; OutBut.printNormal(f.getAbsolutePath().replaceAll(TicklerVars.extractedDir, "[ExtractedDir]")); } if (found) OutBut.printStep("Where [ExtractedDir] is "+ TicklerVars.extractedDir); else OutBut.printNormal("None is found"); } /** * Checking if the app already patched to work in mitm mode on Android 7 * I think I will skip it for now */ public boolean checkTicklerMitmModification(){ File ticklerNSC = new File(TicklerVars.appTickDir+this.resXml+TicklerVars.mitmXmlName); if (ticklerNSC.exists()){ String searchKey=" searchResult = this.searcher.findInFile(new File(TicklerVars.tickManifestFile), searchKey); if (!searchResult.isEmpty()) return true; } return false; } } ================================================ FILE: src/main/java/info/InfoGatheringReporting.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package info; import java.io.File; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.LinkedHashSet; import attacks.StartAttack; import base.OtherUtil; import base.SearchUtil; import cliGui.OutBut; import code.JavaSqueezer; import device.Packagez; import initialization.TicklerVars; import manifest.ManifestDealer; public class InfoGatheringReporting { private ManifestDealer dealer; private StartAttack startAttack; private InfoGathering info; private ArrayList infoArrayList; private Packagez pkgz; public InfoGatheringReporting(){ this.dealer = new ManifestDealer(); this.startAttack = new StartAttack(); this.infoArrayList = new ArrayList(); this.info = new InfoGathering(); this.pkgz = new Packagez(); } public void report() { OutBut.printH1("Package Information"); this.printArrayList(this.pkgz.dumpInfo()); ////////////////////////// SD card /////////////////////////////// this.sdcardChecks(); ///////////////////////// Manifest Analysis ////////////////////////////////// this.manifestAnalysis(); OutBut.printH2("Certificates and Keys found in the APK zipped file"); this.info.getCertificatesInApkDirectory(); } /** * Checks if the app uses external storage */ private void sdcardChecks(){ this.infoArrayList.clear(); if (info.isExternalStorage()){ System.out.println("Code shows that the app uses External storage"); } // search in SD card /sdcard/Android String sdCardPaths =info.getSdcardDirectory(); if (sdCardPaths.isEmpty()){ this.infoArrayList.add("No Directories found for the app in external storage"); } else{ this.infoArrayList.add("Directories found for the app in external storage:"); for (String s:sdCardPaths.split("\n")){ this.infoArrayList.add("\t"+s); } } this.printArrayList(infoArrayList); } ////////////////////////// Manifest Analysis /////////////////////////7 private void manifestAnalysis(){ this.infoArrayList.clear(); this.printDebuggableBackable(); this.isTicklerMitM(); OutBut.printH2("Uses Permissions"); this.printArrayList(this.dealer.manAn.getUsesPermissions()); OutBut.printH2("Components' Permissions"); ArrayList per =this.dealer.manAn.getComponentPermissions(); for (SimpleEntry e:per) { // OutBut.printNormal("Component: "+e.getKey()+"\t\trequests Permission: "+e.getValue()); OutBut.printNormal("Permission: "+e.getValue()+"\n\tis requested by Component: "+e.getKey()); } OutBut.printH2("Content URIs in Code"); this.printArrayList(this.fetchContentUris()); this.printSchemes(); this.getMetaData(); this.findLibs(); } private void printDebuggableBackable(){ this.dealer.analyzeManifest(TicklerVars.tickManifestFile); infoArrayList.add("Backable: "+this.dealer.manAn.getManifest().getApplication().isAllowBackup()); infoArrayList.add("Debuggable: "+this.dealer.manAn.getManifest().getApplication().isDebuggable()); this.printArrayList(this.infoArrayList); } public ArrayList fetchContentUris(){ this.startAttack.provAtt.getContentURIsFromSmali(TicklerVars.smaliDir); return this.startAttack.provAtt.getContentURIs(); } private void printSchemes(){ ArrayList schemes = this.dealer.manAn.getSchemes(); if (!schemes.isEmpty()){ OutBut.printH2("Data Schemes"); schemes = OtherUtil.removeDuplicates(schemes); this.printArrayList(schemes); } } private void isTicklerMitM(){ if (this.info.checkTicklerMitmModification()){ OutBut.printNormal("\nThe app is already patched by Tickler to accept Certificates added by users to Trust store. \n" + "You can use Burp as MitM with the app if installed on Android 7.x"); } //else //OutBut.printNormal("\nThe app is NOT patched by Tickler to work with Burp if installed on Android 7.x device\n" // + "You can run Tickler with -mitm flag to patch the app, if you're using an Android 7.x device"); } private void getMetaData(){ SearchUtil sU = new SearchUtil(); ArrayList result = sU.findInFile(new File(TicklerVars.tickManifestFile), "meta-data"); if (!result.isEmpty()){ OutBut.printH2("Meta-data in Manifest file"); this.printArrayList(result); } } //////////////////////////////////////// private void findLibs() { JavaSqueezer squeeze = new JavaSqueezer(); squeeze.soLibFiles(); squeeze.dllFiles(); } private void printArrayList(ArrayList list){ for (String s:list) System.out.println(s); } } ================================================ FILE: src/main/java/info/ListComponents.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package info; import java.util.ArrayList; import cliGui.OutBut; import code.ExtrasUtil; import components.Action; import components.Category; import components.DataUri; import components.IComponent; import components.Intent; import exceptions.TNotFoundEx; import initialization.TicklerConst; import initialization.TicklerVars; import manifest.ManifestDealer; import manifest.handlers.DataUriHandler; public class ListComponents { private ManifestDealer dealer; public ListComponents() { this.dealer = new ManifestDealer(); } /** * Lists any option given from Tickler * @param compType * @param exported * @param details */ public void listThis(int compType, boolean exported,boolean details){ if (compType==TicklerConst.ALLCOMPS){ for(int i=1;i<5;i++){ this.listType(i, exported,details); } } else{ this.listType(compType, exported,details); } } /** * Lists all components of a certain type * @param compType 1--> 5 * @param exported * @param details */ public void listType(int compType, boolean exported,boolean details){ String expStr=""; boolean isProv = (compType == TicklerConst.PROVIDER); ArrayList names = this.getNamesOfCompTypes(compType, exported); if (exported) expStr="Exported "; else expStr="All "; OutBut.printH1(expStr+TicklerConst.compNames[compType]); if (isProv && !exported) OutBut.printH2("Content Providers in Manifest File"); for (String n:names){ if (details){ this.listComponent(n); } else System.out.println(n); } if (isProv && !exported){ this.listProvidersInCode(); } } public void listComponent(String compName){ this.dealer.analyzeManifest(TicklerVars.tickManifestFile); if (this.dealer.isComponentExist(compName)){ IComponent comp = this.dealer.getComponentByName(compName); if (comp.getIntent()!= null) for (Intent i : comp.getIntent()){ if (i.getData()!= null){ DataUriHandler dh = new DataUriHandler(i); dh.doIt(); } } this.printComponent(comp); this.printExtrasInfo(comp); } else{ OutBut.printError("Component "+compName+" does not exist in the Manifest file"); } } /** * Prints all Content Provider URIs */ private void listProvidersInCode(){ OutBut.printH2("All Content URIs In Code"); InfoGatheringReporting info = new InfoGatheringReporting(); ArrayList prov = info.fetchContentUris(); for (String s : prov){ System.out.println(s); } } /** * Print name, type and whether exported or not * @param comp */ private void printComponent(IComponent comp){ OutBut.printH2(comp.getName()); System.out.println("Type: "+this.getCompClassName(comp)); System.out.println("Exported: "+((comp.isExported())?"True":"False")); this.printIntent(comp); } ///////////////////////////// Intent Filters and Extras /////////////////////////////// /** * Printing intents of a component, assuming its intents are not null * @param comp */ private void printIntent(IComponent comp){ if (comp.getIntent()!=null) { for(Intent i:comp.getIntent()){ System.out.println("\nIntent-Filter:\n--------------"); if (i.getAction()!=null){ this.printActions(i); } if (i.getCategory()!= null){ this.printCategories(i); } if (i.getData()!= null){ this.printData(i); } } } } private void printActions(Intent i){ for (Action a:i.getAction()) System.out.println("Action: "+a.getName()); } /** * Print Categories of an intent * @param i Intent */ private void printCategories(Intent i){ for (Category c:i.getCategory()) System.out.println("Category: "+c.getName()); } /** * Printing the whole data URI (new function) * @param i */ private void printData(Intent i){ DataUriHandler dH = new DataUriHandler(i); System.out.println("Data URI: "); for (DataUri d:i.getData()){ dH.printData(d); System.out.println(); } } /** * Gets Extras Info from CodeUtil class and print the information * @param c */ private void printExtrasInfo(IComponent c){ String cName = c.getName(); ExtrasUtil cU = new ExtrasUtil(); ArrayList extras = cU.getExtrasInfo(cName); if (extras.size()>0){ System.out.println("\nExtras:\n--------"); for (String s : extras) System.out.println(s); } } /////////////////// Util private String getCompClassName(IComponent c){ String name=""; String className = c.getClass().getName(); if (className.equals("components.Activity")) name="Activity"; else if (className.equals("components.Service")) name="Service"; else if (className.equals("components.Receiver")) name="Broadcast Receiver"; else if (className.equals("components.Provider")) name="Content Provider"; return name; } private ArrayList getNamesOfCompTypes(int type, boolean exported){ ArrayList components = this.dealer.getComponentsOfType(type, exported); ArrayList names= new ArrayList<>(); for (IComponent c:components) names.add(c.getName()); return names; } } ================================================ FILE: src/main/java/initialization/TicklerChecks.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package initialization; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.security.CodeSource; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import apk.Decompiler; import base.FileUtil; import base.OtherUtil; import base.SearchUtil; import base.Tickler; import cliGui.OutBut; import commandExec.Commando; import device.Packagez; import exceptions.TNotFoundEx; import info.InfoGathering; public class TicklerChecks { private Commando commando; private String PKG_NOT_FOUND="Package $P does not exist on the connected device. Run -pkgs to see all installed packages"; public TicklerChecks(){ this.commando = new Commando(); } ////////////////////////Initiatlization /////////////////////// /** * Initializazion: * 1) check devices (offline checks migrated to initializeTicklerNoDevice * 2)host requirements (tools exist) * 3) load configurations from conf file * 4) Update TicklerVars * 5) create essential dirs * 6) check external library directory * @param pkgName * @param isDev * @throws TNotFoundEx */ public void initiaizeTickler(String pkgName) throws TNotFoundEx{ Packagez pkg = new Packagez(); // if (isDev){ this.checkDevices(); // } this.checkRequirements(); this.loadConfiguration(); if (!pkg.isPackageExist(pkgName)){ throw new TNotFoundEx(this.PKG_NOT_FOUND.replaceAll("\\$P", pkgName)); } TicklerVars.updateVars(pkgName); this.createEssentialDirs(); this.checkExternalLibDir(); this.isDex2Jar(); } public void initalizeTicklerNoDevice(String pkgName) throws TNotFoundEx{ // this.checkOfflineFeasibility(pkgName); this.loadConfiguration(); TicklerVars.updateVars(pkgName); this.checkExternalLibDir(); if (! this.isPkgFoundOffline(pkgName)) throw new TNotFoundEx(pkgName+" has not been processed before by Tickler... \n If you're decompiling it offline:then " + "Create a directory in "+TicklerVars.ticklerDir+" with its package name"); this.createEssentialDirs(); this.isDex2Jar(); } /** * Check if only one device is connected to the host * @throws TNotFoundEx */ public void checkDevices() throws TNotFoundEx{ String command = "adb devices -l"; Commando commando = new Commando(); String op = commando.executeCommand(command); OtherUtil oU = new OtherUtil(); ArrayList devices = oU.getRegexFromString(op, "(model:.*?device:.+$?)"); int eligDevices = devices.size(); if (eligDevices>1) throw new TNotFoundEx("ERROR: 2 or more Android devices are connected to the host, please connect only one Android device."); if (eligDevices == 0) throw new TNotFoundEx("ERROR: No Android devices detected by the host. Execute adb devices -l to check the connected devices"); } /** * Check if the connected device is an emulator (quick and dirty) */ public boolean isEmulator() { String command = "adb devices -l"; Commando commando = new Commando(); String op = commando.executeCommand(command); if(op.toLowerCase().contains("emulator")) return true; return false; } /** * Check if the host has the following tools: adb, sqlite3, strings * @throws TNotFoundEx */ public void checkRequirements() throws TNotFoundEx{ ArrayList toolsCheck = new ArrayList<>(); //adb toolsCheck.add(new SimpleEntry("adb", "adb devices")); //sqlite3 toolsCheck.add(new SimpleEntry("sqlite3", "sqlite3 -version")); //strings toolsCheck.add(new SimpleEntry("strings", "strings -v")); boolean pass = this.checkToolOnHost(toolsCheck); if (!pass) throw new TNotFoundEx("Missing tools on host: Please install the missing tools and rerun the command"); } /** * Executes a sample command of each tool to detect if it exists * ToDo: it prints stack trace which is not cool * @param toolCheck * @return false if any required tool doesn't exist, otehrwise true */ private boolean checkToolOnHost(ArrayList toolCheck) throws TNotFoundEx{ int result=13; String cmd=""; boolean pass=true; for(SimpleEntry s: toolCheck){ cmd = s.getValue().toString(); result = this.commando.executeProcessListPrintOP(cmd, false); if (result != 0){ OutBut.printError("Tool "+s.getKey().toString()+" does not exist on the host, please install it first"); pass=false; } } return pass; } private void isDex2Jar() throws TNotFoundEx { Decompiler dec = new Decompiler(); if (!dec.isDex2Jar()) throw new TNotFoundEx("Cannot run Dex2Jar tool. Make sure it exists in "+TicklerVars.libNotJarLib+" directory and that d2j-dex2jar.sh and d2j_invoke.sh are executable"); } /** * Load configurations from the config file */ public void loadConfiguration(){ String jarLoc = this.getJarLocation(); TicklerVars.jarPath = jarLoc; TicklerVars.configPath=TicklerVars.jarPath+TicklerConst.configFileName; //Read configs from conf file if (new File(TicklerVars.configPath).exists()){ try { String line; BufferedReader reader = new BufferedReader(new FileReader(TicklerVars.configPath)); while ((line =reader.readLine())!= null) { if (line.contains("Tickler_local_directory")){ String loc = line.substring(line.indexOf("=")+1, line.length()); TicklerVars.ticklerDir = this.correctJarLoc(loc); } else if (line.contains("Tickler_sdcard_directory")){ String loc = line.substring(line.indexOf("=")+1, line.length()-1); TicklerVars.sdCardPath = this.correctJarLoc(loc); } else if (line.contains("Frida_server_path")){ String loc = line.substring(line.indexOf("=")+1, line.length()); TicklerVars.fridaServerLoc = loc; } } reader.close(); } catch (Exception e) { e.printStackTrace(); } } //Config path does not exist else { System.out.println("WARNING...... Configuration file does not exist!!!!\nThe following default configurations are set:\n"); TicklerVars.ticklerDir = TicklerVars.jarPath+TicklerConst.defaultTicklerDirName; System.out.println("Tickler Workspace directory on host: "+TicklerVars.ticklerDir); System.out.println("Tickler temporary directory on device: "+TicklerConst.sdCardPathDefault); } String x = TicklerVars.ticklerDir; if (TicklerVars.ticklerDir == null || TicklerVars.ticklerDir.matches("\\s*/") ){ TicklerVars.ticklerDir = TicklerVars.jarPath+TicklerConst.defaultTicklerDirName; // OutBut.printWarning("Configuration File "+TicklerVars.configPath+ " doesn't specify Tickler_local_directory. Workspace is set at "+ TicklerVars.ticklerDir); OutBut.printStep("Tickler Workspace directory on host: "+TicklerVars.ticklerDir); } if (TicklerVars.sdCardPath == null || TicklerVars.sdCardPath.matches("\\s*/")) { TicklerVars.sdCardPath = TicklerConst.sdCardPathDefault; // OutBut.printWarning("Configuration File "+TicklerVars.configPath+ " doesn't specify Tickler's temp directory on the device. It is set to "+ TicklerVars.sdCardPath); OutBut.printStep("Tickler temporary directory on device: "+TicklerConst.sdCardPathDefault); } } public String getJarLocation(){ File myJar; try{ // File myJar1 = new File(System.getProperty("java.class.path")); myJar = new File(ClassLoader.getSystemClassLoader().getResource(".").getPath()); // myJar = myJar1.getAbsoluteFile().getParentFile(); } catch(Exception e){ myJar = new File("."); } String jarLoc = this.correctJarLoc(myJar.getAbsolutePath()); return jarLoc; } public void getLibName(){ SearchUtil searcher = new SearchUtil(); String[] ext = {"jar"}; String x = TicklerVars.jarPath; String JarName = searcher.search4FileInDir(TicklerVars.jarPath, ext ).get(0).getName(); } /** * Checks if the tool has external Lib directory or a stand alone Jar file * @return */ private void checkExternalLibDir() throws TNotFoundEx{ String jarLoc = this.getJarLocation(); String libDirLoc=jarLoc+TicklerConst.generalLibName; File tickLib = new File (libDirLoc); if (tickLib.exists()){ TicklerVars.isLib = true; TicklerVars.libDir = libDirLoc; } //Lib directory not found else { throw new TNotFoundEx("Lib directory not found. \nMake sure that "+TicklerConst.generalLibName+" directory exists in the same directory as Tickler.jar"); } } public String correctJarLoc(String jarLoc){ String finalLoc=jarLoc; if (jarLoc.contains(":")) finalLoc = jarLoc.substring(0, jarLoc.indexOf(":")); Matcher m = Pattern.compile("\\s+(.+)").matcher(jarLoc); if (m.find()) finalLoc = m.group(1); if (finalLoc.matches(".+\\n$")){ finalLoc = finalLoc.substring(0, jarLoc.length()-1); } if (finalLoc.matches(".+\\.$")){ finalLoc = finalLoc.substring(0, jarLoc.length()-1); } if (!finalLoc.matches(".+/$")){ finalLoc = finalLoc+"/"; } return finalLoc; } /** * Create sdCardPath and App directory in TicklerDir if they don't exist. */ private void createEssentialDirs() throws TNotFoundEx{ FileUtil fT = new FileUtil(); fT.createDirOnDevice(TicklerVars.sdCardPath); if (!fT.isExistOnDevice(TicklerVars.sdCardPath)) throw new TNotFoundEx("Cannot create Tickler directory "+TicklerVars.sdCardPath+". Check your configurations in Tickler.conf file"); fT.createDirOnHost(TicklerVars.appTickDir); if (!fT.isExist(TicklerVars.appTickDir)) throw new TNotFoundEx("Cannot create Tickler directory "+TicklerVars.appTickDir+". Check your configurations in Tickler.conf file"); } private boolean isPkgNameExist(String pkgName){ Packagez pkgz = new Packagez(); return pkgz.isPackageExist(pkgName); } /** * Checks if the tickler command is possible to be executed without the device * Migrated to TicklerCli as a quick and dirty solution * @param command */ // public void checkOfflineFeasibility(String command) { // String [] offlineCommands = {"sc", "sd", "apk", "mitm", ""}; // } private boolean isPkgFoundOffline(String pkgName) { FileUtil ft = new FileUtil(); String pkgDir = TicklerVars.ticklerDir+pkgName; if (ft.isExist(pkgDir)) return true; return false; } } ================================================ FILE: src/main/java/initialization/TicklerConst.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package initialization; public class TicklerConst { //Files and Directories names public static String configFileName="Tickler.conf"; public static String generalLibName = "libs/"; public static String jarsLibName = "jars/"; public static String notJarsLibName = "notJars/"; public static String defaultTicklerDirName="Tickler_workspace/"; public static String sdCardPathDefault = "/sdcard/Tickler/"; public static String MANIFEST_NAME = "AndroidManifest.xml"; public static String DATA_DIR_NAME = "DataDir/"; public static String EXTRACTED_NAME = "apk/"; public static String SMALI_DIR_NAME = "smali/"; public static String IMAGES_DIR_NAME= "images/"; public static String LOGS_DIR_NAME = "logs/"; public static String BG_DIR_NAME = "bgSnapshots/"; public static String TRANSFER_DIR_NAME = "transfers/"; public static String DEX2JAR_OP_DIR_NAME= "JavaCode/"; public static String FRIDA_SCRIPTS_DIR_NAME= "fridaScripts/"; public static String JAVA_CODE_DIR_NAME = DEX2JAR_OP_DIR_NAME+"Code"; public static String newAppTempDir = "newAppTemp/"; public static String EXTERNAL_STORAGE_Dir="ExtDataDir/"; public static String DIFF_OLD_STORAGE = "Storage_old/"; public static String DATA_DIR_OLD=DIFF_OLD_STORAGE+DATA_DIR_NAME; public static String EXT_DIR_OLD=DIFF_OLD_STORAGE+EXTERNAL_STORAGE_Dir; public static String SQUEEZE_FILE_NAME= "squeeze.txt"; public static String APKTOOL_FILE_NAME="apktool.jar"; //Tools names public static String DEX2JAR_EXEC = "/dex2jar-2.1/d2j-dex2jar.sh"; public static String KEY_STORE_DIR_NAME = "Keystore/Tickler.keystore"; public static int ALLCOMPS=0; public static int ACTIVITY=1; public static int SERVICE=2; public static int PROVIDER=3; public static int RECEIVER=4; public static String version="2.7"; public static final int debuggable=0; public static final int mitm=1; public static String mitmApkName = "mitm.apk"; public static String debuggableName = "debuggable.apk"; public static String mitmXmlName="tickler_network_security_config"; public static String[] compNames ={"All components","Activities", "Services", "Content Providers", "Broadcast Receivers"}; public static String helpMessage="\n usage: Tickler \n" +" ==============\n" +" Please make sure that the configuration file: Tickler.conf is at the same directory as\n" +" the Tickler .jar file. Tickler.conf file defines Tickler's directory on the host and the\n" +" temp directory on the android device.\n\n" +" Options\n" +" ---------\n" +" -h, --help Prints this message\n" +" -screen Take a screenshot of the device\n" +" -findPkg Search for a package name \n" +" -version Print version \n" +" -pkgs List all installed packages on the android device \n" +" -offline Offline Mode, no devices needed to be connected\n" +" -pkg Specify package name of the app \n" +" \n" +" Options of pkg: \n" +" -------------- \n" +" -info List information about the app \n" +" -squeeze All strings, log functions, possible credentials in decompiled APK \n" +" -squeeze short Limits the squeezed code to the developer's code (most libraries excluded) \n" +" -dbg, --debuggable Create a debuggable version of the app \n" +" -apk Compiles a new apk from directory under the name of \n" +" -dataDir [name] Copies data directory of app to Tickler Directory (DataDir or transfers/name) \n" +" -diff Copies app's data directory before and after a user's action, then diffs between them\n" +" -diff [d| detailed] like diff, but also shows the changes in case a text file or an unecrypted database is changed\n" +" -db [option] [-nu] Database checks. By default app's Data Dir is copied to host before checks\n" +" [-nu | --noUpdate] Does not update DataDir on host before checks \n" +" [option] can be:\n" +" [ |e|encryption] Checks whether the database files of the app are encrypted or not\n" +" [l |list] Lists Databases of the app \n" +" [d |dump] Dumps an unencrypted database\n" +" -sc, --searchCode Search for \"key\" in the app's decompiled Java code\n" +" -sd, --searchDataDir Search for \"key\" in the app's Data Directory\n" +" -t [target] [-log] Tickels a target. The type of the attack depends on the target \n" +" -log Captures logcat messages \n" +" -l,--list [target] [-v] List components of type target \n" + " -v List component(s) in details\n" +" \n" +" \n" +" Targets: \n" +" -------- \n" +" -act,--activities Activities \n" +" -ser,--services Services \n" +" -prov,--providers Content providers \n" +" -rec,--receivers Broadcast receivers \n" +" [ | -comp] A specific component, its name has to be exactly as in Manifest file (also displayed by -l -all) \n" +" -exp Applies [trigger | list] action to exported components only \n" +" [ | -all] All components (default if none of the above targets are specified) \n" +" \n" + " Options that work with and without -pkg option\n" + " -----------------------------------------------\n" + " -screen Captures a screenshot of the device\n" + " -cp2host [destName] Copies any file/directory to the tickler's app directoy on the host\n" + " -bg,--bgSnapshots Copies background screenshots that are saved on the device\n\n" + " Frida \n " + "----- \n " + "-frida enum \n " + " Enumerates loaded classes \n " + "-frida vals [-reuse] \n " + " Displays arguments and return value of this method (only primitive datatypes and String) \n " + " \n " + "-frida set [-reuse] \n " + " Sets the argument number to (only primitive datatypes and String) \n " + " If > : sets the return value \n " + " \n " // + "-frida unpin \n " // + " SSL pinning circumvention as in https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida/ \n " // + " is the location of your certificate on your host\n\n " + "-frida script \n " + " Run custom frida JS script \n\n" +" Examples: \n" +" --------- \n" +" 1) List all components of package com.test.package, with detailed information \n" +" ----------------------------------------------------------------------------- \n" +" java -jar Tickler.jar -pkg com.test.package -l -v \n" +" \n" +" 2) Trigger exported activities of package com.test.package \n" +" ----------------------------------------------------------- \n" +" java -jar Tickler.jar -pkg com.test.package -t -act -exp \n\n" +" 3) Squeeze app's decompiled Java code for strings, log messages...etc\n" +" ----------------------------------------------------------------------- \n" +" java -jar Tickler.jar -pkg com.test.package -squeeze > output.txt \n" ; } ================================================ FILE: src/main/java/initialization/TicklerVars.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package initialization; public abstract class TicklerVars { public static String ticklerDir; public static String pkgName; public static boolean isLib,isOffline; public static String jarPath, configPath; public static String appTickDir,sdCardPath, tickManifestFile,extractedDir,dataDir ,smaliDir, imageDir, logDir, bgSnapshotsDir,transferDir, libDir, libJarDir, libNotJarLib, dex2jarPath, apktoolPath, dex2jarDir,jClassDir,keyStore,newApkTempDir,mitmXmlName, fridaServerLoc, fridaScriptsDir,extDataDir, squeezeFile; public static String version="2.2"; public static void setPkgName(String pName){ if (TicklerVars.pkgName!=null && !TicklerVars.pkgName.equals(pName)){ // System.out.println("WARNING: Changing package name from "+TicklerVars.pkgName+" to "+pName); } TicklerVars.pkgName = pName; } public static void updateVars(String pName){ setPkgName(pName); appTickDir = ticklerDir+pkgName+"/"; tickManifestFile = appTickDir+TicklerConst.MANIFEST_NAME; sdCardPath = TicklerConst.sdCardPathDefault+pkgName +"/"; dataDir = appTickDir+TicklerConst.DATA_DIR_NAME; extractedDir = appTickDir+TicklerConst.EXTRACTED_NAME; smaliDir = extractedDir+TicklerConst.SMALI_DIR_NAME; imageDir= appTickDir+TicklerConst.IMAGES_DIR_NAME; logDir = appTickDir+TicklerConst.LOGS_DIR_NAME; bgSnapshotsDir = appTickDir+TicklerConst.BG_DIR_NAME; transferDir = appTickDir+TicklerConst.TRANSFER_DIR_NAME; extDataDir = appTickDir+TicklerConst.EXTERNAL_STORAGE_Dir; libDir=jarPath+TicklerConst.generalLibName; libJarDir=libDir+TicklerConst.jarsLibName; libNotJarLib=libDir+TicklerConst.notJarsLibName; // Will be set anyway whether they exist or not dex2jarPath = libNotJarLib+TicklerConst.DEX2JAR_EXEC; dex2jarDir = appTickDir+TicklerConst.DEX2JAR_OP_DIR_NAME; apktoolPath = libJarDir+TicklerConst.APKTOOL_FILE_NAME; // Output of Java classes jClassDir = appTickDir+TicklerConst.JAVA_CODE_DIR_NAME; keyStore = libNotJarLib+TicklerConst.KEY_STORE_DIR_NAME; newApkTempDir = appTickDir+TicklerConst.newAppTempDir; fridaScriptsDir = appTickDir+TicklerConst.FRIDA_SCRIPTS_DIR_NAME; squeezeFile = appTickDir+TicklerConst.SQUEEZE_FILE_NAME; } public static void setTicklerDir(String dir){ ticklerDir = dir; } public static String replaceSpace(String s){ return s.replaceAll("\\s", "\\\\s"); } } ================================================ FILE: src/main/java/logs/LogReader.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package logs; import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; import base.FileUtil; import commandExec.Commando; import initialization.TicklerVars; public class LogReader implements Runnable { String logFileName; Commando commando; FileWriter writer; private LogReaderController controller; public LogReader(LogReaderController controller){ this.commando = new Commando(); this.controller = controller; this.logFileName = this.controller.getLogFileName(); } @Override public void run() { this.readWriteLogCat(); } public void readWriteLogCat(){ File logFile = new File(this.logFileName); String command = "adb logcat"; this.executeLogcat(command, logFile); } public void executeLogcat(String command, File logFile){ Process process; try { process=Runtime.getRuntime().exec(command); BufferedReader reader = new BufferedReader(new InputStreamReader( process.getInputStream())); this.writer = new FileWriter(logFile,true); String line = ""; while (((line = reader.readLine())!= null) && !this.controller.isStop()) { synchronized(this){ this.writer.append(line+"\n"); this.writer.flush(); } } } catch(Exception e) { e.printStackTrace(); } if (this.controller.isStop()) this.closeWriter(); } public void closeWriter(){ try{ this.writer.close(); } catch(IOException e) {} } public String getLogFileName() { return logFileName; } public void setLogFileName(String logFileName) { this.logFileName = logFileName; } } ================================================ FILE: src/main/java/logs/LogReaderController.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package logs; public class LogReaderController { private String logFileName; private boolean stop; public String getLogFileName() { return logFileName; } public void setLogFileName(String logFileName) { this.logFileName = logFileName; } public boolean isStop() { return stop; } public void setStop(boolean stop) { this.stop = stop; } } ================================================ FILE: src/main/java/manifest/ManifestAnalyzer.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package manifest; import java.util.ArrayList; import java.util.List; import java.util.AbstractMap.SimpleEntry; import components.Activity; import components.DataUri; import components.IComponent; import components.Intent; import components.Manifest; import components.Provider; import components.Receiver; import components.Service; import components.UsesPermission; import initialization.TicklerConst; import initialization.TicklerVars; import manifest.handlers.IntentHandler; /** * This class analyzes the Manifest file and extracts information from it * @author aabolhadid * */ public class ManifestAnalyzer { private Manifest manifest; private ArrayList components,exported,hidden; public ManifestAnalyzer(Manifest manifest) { this.components = new ArrayList(); this.manifest = manifest; this.collectAllComponents(); this.collectExportedComponents(); } public void collectAllComponents() { this.components.addAll(this.manifest.getApplication().getActivites()); if (this.manifest.getApplication().getServices() != null) this.components.addAll(this.manifest.getApplication().getServices()); if (this.manifest.getApplication().getProviders() != null) this.components.addAll(this.manifest.getApplication().getProviders()); if (this.manifest.getApplication().getReceivers() != null) this.components.addAll(this.manifest.getApplication().getReceivers()); } /** * Divides all components over exported and hidden lists */ public void collectExportedComponents() { this.exported = new ArrayList(); this.hidden = new ArrayList(); // Probably not needed because isExposed is decided during parsing XML // this.decideExportedStatus(); for(IComponent c : this.components) { if (c.isExported()) this.exported.add(c); else this.hidden.add(c); } } /** * Sets isExported, based on exported field in manifest and existence of implicit intents * Not called anymore because the decision on exported or not happens during parsing hte manifest file */ // public void decideExportedStatus() { // for(IComponent c : this.components) { // if ( c.isExported()) // c.setExported(true); // else if (c.isExported()!= null && c.isExported()) // c.setExported(true); // else if (c.getExp()==null && c.getIntent()!=null) // c.setExported(true); // else // c.setExported(false); // } // } public IComponent getComponentByName(String compName){ String name; for (IComponent c:this.components){ name = c.getName(); if (name.equals(compName)) return c; } return null; } public ArrayList getComponentsOfType(int i, boolean exp) { ArrayList list = new ArrayList(); for (IComponent c:this.components){ if (!exp || (exp && c.isExported())){ if ( i ==TicklerConst.ACTIVITY&& c instanceof Activity) list.add(c); else if (i ==TicklerConst.SERVICE && c instanceof Service) list.add(c); else if (i == TicklerConst.PROVIDER && c instanceof Provider) list.add(c); else if (i == TicklerConst.RECEIVER && c instanceof Receiver) list.add(c); } } return list; } public ArrayList getUsesPermissions(){ ArrayList result = new ArrayList<>(); for (UsesPermission p: this.getManifest().getUsesPermissions()){ result.add(p.getName()); } return result; } /** * New: Get Permissions defined in all components * @return ArrayList> */ public ArrayList getComponentPermissions(){ ArrayList permissions = new ArrayList(); for (IComponent c:this.components) { if(c.getPermission()!=null && ! c.getPermission().isEmpty()) { SimpleEntry e = new SimpleEntry(c.getName(), c.getPermission()); permissions.add(e); } } return permissions; } public ArrayList getSchemes(){ List intents; ArrayList schemes = new ArrayList(); ArrayList data = new ArrayList<>(); for (IComponent c : this.components){ if ((intents =c.getIntent()) != null){ for (Intent i:intents){ if (i.getData()!=null) data.addAll(i.getData()); } } } for(DataUri d:data){ schemes.add(d.getScheme()); } return schemes; } //////////////////////// Getters and Setters //////////////////////// public Manifest getManifest() { return manifest; } public void setManifest(Manifest manifest) { this.manifest = manifest; } public ArrayList getComponents() { return components; } public void setComponents(ArrayList components) { this.components = components; } public ArrayList getExported() { return exported; } public void setExported(ArrayList exported) { this.exported = exported; } public ArrayList getComponents(boolean exp) { if (exp){ return this.getExported(); } return components; } public ArrayList getHidden() { return hidden; } public void setHidden(ArrayList hidden) { this.hidden = hidden; } } ================================================ FILE: src/main/java/manifest/ManifestDealer.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package manifest; import java.io.File; import java.util.ArrayList; import apk.ApkToolClass; import apk.ApkToolDude; import apk.Decompiler; import base.DOMXMLReader; import base.FileUtil; import cliGui.OutBut; import commandExec.Commando; import components.Activity; import components.IComponent; import components.Manifest; import exceptions.TNotFoundEx; import initialization.TicklerConst; import initialization.TicklerVars; /** * Dunno how to call this class. * !!!!!!!!!!!! So far, it's the class that gives back Tickler its Manifest !!!!!!!!!!!!!! * @author aabolhadid * */ public class ManifestDealer { private String manifestPath,pkgName,appTickDir; private DOMXMLReader domXmlreader; public ManifestAnalyzer manAn; private FileUtil fileT; private ApkToolDude apkTool; private boolean apkExisted = true; /////////////////////// Package option ///////////////////////// public void meetThePackage(String pkgName){ this.pkgName = pkgName; fileT = new FileUtil(); this.appTickDir = TicklerVars.appTickDir; // New ApkClass // this.apkTool = new ApkToolClass(); this.apkTool = new ApkToolDude(); //1- create apk tickler directory if it does not exist fileT.createDirOnHost(this.appTickDir); //2- Copy APK from device if it does not exist in the app tickler directory this.copyApkFromDevice(); //3- Extract Manifest from APK if Manifest file does not exist if (!new File(this.appTickDir+"/AndroidManifest.xml").exists()) { String apkName = this.getApkName(); String x = this.appTickDir+apkName; try { this.apkToolDecode(x); OutBut.printStep("Extracting Manifest file from APK"); } catch(TNotFoundEx e){ OutBut.printError("Cannot extract Manifest file from APK"); } } //4- Compile the apk using Dex2Jar this.dex2Jar(); } /** * Decompile the APK using dex2jar tool */ private void dex2Jar(){ if (!new File(TicklerVars.jClassDir).exists()){ Decompiler d2j = new Decompiler(); d2j.decompile(); } } /** * If APK does not exist in the package directory on the host, then Ticker fetches the apk */ private void copyApkFromDevice() { if (!this.isApkExist()){ OutBut.printStep("Copying APK file from the device"); fileT.copyDirToHost(this.getPackageApkPath(), this.appTickDir,true); this.apkExisted = false; } } private boolean isApkExist(){ if (TicklerVars.isOffline) { String [] apkArray = {"apk"}; if (this.fileT.listFilesInDirContain(TicklerVars.appTickDir, apkArray )!= null) return true; } String apkPath = this.getPackageApkPath(); String apkName = this.getFileNameFromPath(apkPath); File apk = new File(TicklerVars.appTickDir+apkName); return apk.exists(); } ///////////////////// APK tool Encapsulation /////////////////////// private void apkToolDecode(String apkPath) throws TNotFoundEx{ OutBut.printStep("Decompiling the APK file using APKTool"); this.apkTool.apkToolDecode(apkPath); } private String getPackageApkPath(){ String command = "pm path "+this.pkgName; Commando c = new Commando(); String result = c.execADB(command); return result.substring(result.indexOf(":")+1,result.indexOf("\n")); } public String getApkName(){ String apkName2; if (TicklerVars.isOffline) { //Name will always be base.apk apkName2 = "base.apk"; } else { String apkName = (new File(this.getPackageApkPath())).getName(); apkName2 = apkName.substring(0,apkName.indexOf(".apk")+4); String filePath = this.appTickDir+"/"+apkName2+".apk"; } return apkName2; } /////////////////////////////// Common: For all //////////////////////////////// public void analyzeManifest(String manifestPath){ Manifest theManifest = this.generateManifestFromXML(manifestPath); this.setPkgName(TicklerVars.pkgName); this.manAn = new ManifestAnalyzer(theManifest); } // Modified : replacing xmlreader with DOMXmlreader public Manifest generateManifestFromXML(String path){ // this.xmlreader = new XMLReader(path); this.domXmlreader = new DOMXMLReader(path); Manifest theManifest = this.domXmlreader.parselManifest(); return theManifest; // return this.xmlreader.getManifest(); } private String getFileNameFromPath(String path){ File absPath = new File(path); String theName = absPath.getName(); if (absPath.isDirectory()) theName=theName+"/"; return theName; } /////////////// Components ////////////// public ArrayList getComponentsOfType(int i, boolean exp) { this.analyzeManifest(TicklerVars.tickManifestFile); if (i == TicklerConst.ALLCOMPS) return this.manAn.getComponents(exp); return this.manAn.getComponentsOfType(i,exp); } /** * Checks if a component with this name exists * @param compName * @return */ public boolean isComponentExist(String compName){ if (this.manAn.getComponentByName(compName) != null) return true; else return false; } /** * Returns a component of a given name, should be used if isComponentExist = true * @param compName * @return */ public IComponent getComponentByName(String compName){ return this.manAn.getComponentByName(compName); } ////////////////////// Getters and setters public String getPkgName() { return pkgName; } public void setPkgName(String pkgName) { if (TicklerVars.pkgName!=null && !TicklerVars.pkgName.equals(pkgName)){ System.out.println("WARNING: Changing package name from "+TicklerVars.pkgName+" to "+pkgName); } this.pkgName = pkgName; } public String getManifestPath() { return this.manifestPath; } public void setManifestPath(String path) { this.manifestPath = path; } public boolean wasApkExist() { return apkExisted; } } ================================================ FILE: src/main/java/manifest/handlers/DataUriHandler.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package manifest.handlers; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import base.TicklerGeneral; import components.DataUri; import components.Intent; public class DataUriHandler { private ArrayList totalDU, outOfMishMash; private Intent intent; private ArrayList> collection; public DataUriHandler(Intent intent){ this.totalDU = new ArrayList<>(); this.outOfMishMash = new ArrayList<>(); this.intent = intent; this.collection = new ArrayList<>(); } public void doIt(){ this.collection = this.collectMap(); this.totalDU = this.createDUsForSchemes(); this.addDataUriComp("host"); this.addDataUriComp("port"); this.addDataUriComp("path"); this.addDataUriComp("pathPattern"); this.addDataUriComp("pathPrefix"); this.addDataUriComp("mimeType"); // If there are only dataURI tags with only MimeType this.addMimeTypeOnly(); this.addCompleteToMishmash(); } private ArrayList> collectMap(){ ArrayList> collection = new ArrayList<>(); for (DataUri d:this.intent.getData()){ Map h = d.getDataMap(); collection.add(h); } return collection; } /** * Creates an array of DataUris, one for each scheme * Problem: takes only the scheme !!! * @return */ private ArrayList createDUsForSchemes(){ ArrayList arr = new ArrayList<>(); for (Map h: this.collection){ if (h.get("scheme")!= null){ DataUri d = this.getDataUriFromMap(h); if (this.isMapComplete(h)) this.outOfMishMash.add(d); else arr.add(d); } } return arr; } /** * If the map has enough components to be independent : scheme & host, it will be excluded from other Data maps mishmash * @param map * @return */ private boolean isMapComplete(Map map){ if (map.get("scheme")!=null && map.get("host")!=null) return true; return false; } private void addCompleteToMishmash(){ this.totalDU.addAll(outOfMishMash); } private void addMimeTypeOnly(){ if (this.isMimeTypeOnly()){ DataUri dTypeOnly = new DataUri(); this.totalDU.add(dTypeOnly); this.addDataUriComp("mimeType"); } } private boolean isMimeTypeOnly(){ for (Map h: this.collection){ if (!(h.containsKey("mimeType") && h.size()==1)) return false; } return true; } private void addDataUriComp(String compName){ for (Map h: this.collection){ if (h.get(compName)!= null){ this.updateTotalDU(compName, h); } } } private void updateTotalDU(String compName, Map h){ ArrayList tempDataUri = new ArrayList<>(); for (DataUri d:this.totalDU) { DataUri d2 = this.copyDataUri(d); if (!this.isDataUriCompEmpty(compName, d)){ tempDataUri.add(d); if (this.isPathConflict(compName,d)){ this.clearPathValuesCuzConflict(d2); } } d2 = this.addCompByType(compName,h.get(compName),d); tempDataUri.add(d2); } this.totalDU = tempDataUri; } /** * If there is a path conflict, delete all path properties in order to add the new one later * @param d */ private void clearPathValuesCuzConflict(DataUri d){ d.setPath(null); d.setPathPattern(null); d.setPathPrefix(null); } /** * Added the occupied check to make sure that if multiple properties are defined in a single Data Tag, they will not be replaced (Not a solution asasan) * @param name * @param value * @param d * @return */ private DataUri addCompByType(String name,String value,DataUri d){ DataUri dTemp = this.copyDataUri(d); switch(name) { case "scheme": dTemp.setScheme(value); break; case "host": dTemp.setHost(value); break; case "port": dTemp.setPort(value); break; case "path": dTemp.setPath(value); break; case "pathPattern": dTemp.setPathPattern(value); break; case "pathPrefix": dTemp.setPathPrefix(value); break; case "mimeType": dTemp.setMimeType(value); break; } return dTemp; } /** * Check if a map of a dataUri contains a DataUri component * @param name * @param h * @return */ private boolean isDataUriCompEmpty(String name, DataUri d) { Map h = d.getDataMap(); return (h.get(name)== null); } /** * If the component is a path* component and d has another path* component, return true * Bardo not a solution :( * @param compName * @param d * @return */ private boolean isPathConflict(String compName,DataUri d){ if (compName.contains("path") && (d.getPath()!= null || d.getPathPattern()!= null || d.getPathPrefix()!= null)) return true; return false; } private DataUri getDataUriFromMap(Map h){ DataUri d = new DataUri(); String[] comps = {"scheme","host","port","path","pathPattern","pathPrefix","mimeType"}; for(String s:comps){ d = this.addCompByType(s, h.get(s), d); } return d; } private DataUri copyDataUri(DataUri d){ DataUri d2 = new DataUri(); d2.setHost(d.getHost()); d2.setMimeType(d.getMimeType()); d2.setPath(d.getPath()); d2.setPathPattern(d.getPathPattern()); d2.setPathPrefix(d.getPathPrefix()); d2.setPort(d.getPort()); d2.setScheme(d.getScheme()); return d2; } /** * Prints Data URI component of an intent, used with -l * @param d */ public void printData(DataUri d) { if (d.getScheme()!=null) System.out.println("\tScheme: "+d.getScheme()); if (d.getHost()!=null) System.out.println("\tHost: "+d.getHost()); if (d.getPort()!=null) System.out.println("\tPort: "+d.getPort()); if (d.getPath()!=null) System.out.println("\tPath: "+d.getPath()); if (d.getPathPattern()!=null) System.out.println("\tPath Pattern: "+d.getPathPattern()); if (d.getPathPrefix()!=null) System.out.println("\tPath Prefix: "+d.getPathPrefix()); if (d.getMimeType()!=null) System.out.println("\tMime Type: "+d.getMimeType()); } /** * Prepare the data URI part in am start command * @param d * @return */ public String getStartCommand(DataUri d) { String cmd=""; if (d.getScheme()!=null && ! d.getScheme().isEmpty()) { cmd = " -d \""+d.getScheme()+"://"; TicklerGeneral.schemes.add(d.getScheme()); } // If scheme is not in the same data entry, it might be declared in a previous or a next one. else if (!TicklerGeneral.schemes.isEmpty() /*&& !TicklerGeneral.schemes.contains("://")*/) { cmd = " -d \""+TicklerGeneral.schemes.get(0)+"://"; } //Therefore add a dummy $scheme now and we'll replace it later else { cmd = " -d \"$scheme://"; } // If a scheme is not specified for the intent filter, all the other URI attributes are ignored. // https://developer.android.com/guide/topics/manifest/data-element // Update: 10.21 --> This is false, because scheme could be defined in another XML entity. Don't ignore if (d.getHost()!=null && !d.getHost().isEmpty()){ String newVal=this.replaceAstrexInPathValues(d.getHost()); cmd = cmd+newVal+"/"; } if (d.getPort()!=null && !d.getPort().equals("*") && !d.getPort().isEmpty()) cmd = cmd+ ":"+d.getPort(); if (d.getPath()!=null){ String newVal = this.replaceAstrexInPathValues(d.getPath()); cmd = cmd+newVal; } if (d.getPathPattern()!=null){ String newVal = this.replaceAstrexInPathValues(d.getPathPattern()); cmd = cmd + newVal; } if (d.getPathPrefix()!=null && !d.getPathPrefix().equals("*")) cmd = cmd+ d.getPathPrefix(); //} if (!cmd.isEmpty()) { cmd = cmd+"TiCkLeR\""; } if (d.getMimeType()!=null && ! d.getMimeType().isEmpty()) cmd = cmd+" -t " +d.getMimeType(); //if dataURI is empty then cmd is empty. It will be removed later if (cmd.equals(" -d \"$scheme://TiCkLeR\"")) { cmd=""; } return cmd; } /** * REplace the wildcards in Path values (prefix and pattern) with a dummy value * @param path * @return */ private String replaceAstrexInPathValues(String path){ String newPath = path.replace(".*", "TiCkLeR"); String newPath2 = newPath.replace("\\", ""); newPath = newPath2.replace("*", "TiCkLeR"); return newPath; } /** * Gets All Data URI combinations * @return */ public ArrayList getTotalDU() { return totalDU; } public void setTotalDU(ArrayList totalDU) { this.totalDU = totalDU; } } ================================================ FILE: src/main/java/manifest/handlers/IntentHandler.java ================================================ /******************************************************************************* * Copyright 2017 Ahmad Abolhadid * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package manifest.handlers; import java.util.ArrayList; import base.TicklerGeneral; import components.Action; import components.Category; import components.DataUri; import components.Intent; public class IntentHandler { private String command; private Intent intent; private ArrayList commands,additionalCommands; public IntentHandler(String origCommand,Intent intent) { this.command = origCommand; this.intent = intent; this.commands = new ArrayList(); this.additionalCommands = new ArrayList(); } /** * Gets all combinations of Intent actions,and DAta URI * For now: the categories are excluded * @return */ public ArrayList fullIntent() { this.commands.add(this.command); this.addActions(); this.addData(); return this.commands; } private void addActions() { if (this.intent.getAction()!=null) { for (Action a : this.intent.getAction()) this.updateCommandsList(" -a "+a.getName()); this.commands.addAll(this.additionalCommands); this.additionalCommands.clear(); } } private void addData(){ if (this.intent.getData() != null) { DataUriHandler dH = new DataUriHandler(this.intent); dH.doIt(); ArrayList totalDU = dH.getTotalDU(); for (DataUri d : totalDU){ this.updateCommandsList(dH.getStartCommand(d)); } this.commands.addAll(this.additionalCommands); this.additionalCommands.clear(); TicklerGeneral.schemes.clear(); } } private void updateCommandsList(String s) { for (String str:this.commands){ this.additionalCommands.add(str+s); } } }