The Dexcalibur GUI can be launch from the console by using the dexcalibur script.
The example below shows how start to scan an application:
./dexcalibur --app=<appname> --port=<webapp_port>
For each arguments taking a value, the arguments value should be separated from the argument name by an 'equal' symbol like :
--api=android:7.0.0
All available arguments are explained below :
-Usage: dexcalibur [--api][--pull][--devices][--app][--apk][--port][--config][--help|-h||] [--no-frida][--buildClass][--buildOut][--buildApi]
--api The Android API version to use. It should be one entry of platform_available config option.
--pull To pull the APK file of the targeted application from the device
--devices To list connected devices
--app The targeted application name (if already analyzed)
--apk The path to the target APK file
--port The web server port number
--config The path to a custom config file. Default : ./config.js
--help,-h,, This menu
--no-frida To disable Frida part. It allows to run Dexcalibur to analyze purpose even if Frida is not installed
--buildClass To generate Frida script with a Java.use for each class contained into the specified package (see docs)
--buildOut The output directory
--buildApi To build the representation of the specified Android API
Actually, the analysis is based on the Android API 24 (7.0.0). However, you can pass another Android version like it :
./dexcalibur --api=android:7.0.0 --app=<appname> --port=<webapp_port>
:~$ node --v8-pool-size=4 --max-old-space-size=8192
> var project = require("./quickstart.js").START("com.app.test");
...
> project.find.class().count()
First, open a Node.JS terminal and import the Dexcalibur tool. Depending of the application size, the memory space and thread allow should be customized. (Here 4 threads, 8Gb of memory, see chapter 4 for more details).
:~$ node --v8-pool-size=4 --max-old-space-size=8192
> var Dexcalibur = require("./src/project.js")
> var project = new Dexcalibur("com.targeted.app")
The analyzer performs static analysis by parsing the original binaries and making a model of the application.
This component takes avantage of the Search API and Security Scanner in order to hook obfuscator methods and dynamically update his modelisation at runtime. It allows the analyzer to discover definition contained in encrypted Dex file, and make a more complete image of the application.
If a single device/emulator is connected and installed the packages com.targeted.app, the analyzer can be call as it :
> var project = new Dexcalibur("com.targeted.app")
> project.pull()
> project.fullscan()
If several device/emulator are connected, you should use the Device Manager in order to select the default device for the project :
> var project = new Dexcalibur("com.targeted.app")
...
> project.devices.scan()
╔═══════════════════════════[ Android devices ]═══════════════════════════════╗
║ 1234567890abcdef :Nexus_6 ║
║ 1234567890aaaaaa :LG ║
╚═════════════════════════════════════════════════════════════════════════════╝
> project.devices.setDefault("1234567890abcdef")
> project.scan()
> project.scan('./output_apktool/')
> project.scanFile('./target.apk')
Search API is one of the more complete feature of Dexcalibur. It is the search engine to request the Analyzer database.
Some aims of the search engine are :
- to select a subset of method/field to hook automatically
- to select a subset of string or instruction to patch
- to inspect a method by searching his callers or properties
- to identify string used and interresting assets
- to understand control flow
The search engine is based on two type of search :
- to search into the graph by following cross reference and properties from a set of node
- to filter one or more search results
to list all methods enclosed into a class where FQCN match the regexp .TelephonyManager.
> Project.find.method("enclosingClass.name:TelephonyManager").show()
Running deep search ...
+---------------------------------------------------------------------------------------------------------------------------+
| Index | Class | Modifiers | Method |
+---------------------------------------------------------------------------------------------------------------------------+
| 0 | android.telephony.TelephonyManager | [static,public,constructor] | <clinit> |
| 1 | android.telephony.TelephonyManager | [public,constructor] | <init> |
| 2 | android.telephony.TelephonyManager | [public] | getAllCellInfo |
| 3 | android.telephony.TelephonyManager | [public] | getCallState |
| 4 | android.telephony.TelephonyManager | [public] | getCellLocation |
| 5 | android.telephony.TelephonyManager | [public] | getDataActivity |
| 6 | android.telephony.TelephonyManager | [public] | getDataState |
| 7 | android.telephony.TelephonyManager | [public] | getDeviceId |
| 8 | android.telephony.TelephonyManager | [public] | getDeviceSoftwareVersion |
| 9 | android.telephony.TelephonyManager | [public] | getGroupIdLevel1 |
| 10 | android.telephony.TelephonyManager | [public] | getLine1Number |
| 11 | android.telephony.TelephonyManager | [public] | getMmsUAProfUrl |
| 12 | android.telephony.TelephonyManager | [public] | getMmsUserAgent |
| 13 | android.telephony.TelephonyManager | [public] | getNeighboringCellInfo |
| 14 | android.telephony.TelephonyManager | [public] | getNetworkCountryIso |
| 15 | android.telephony.TelephonyManager | [public] | getNetworkOperator |
| 16 | android.telephony.TelephonyManager | [public] | getNetworkOperatorName |
| 17 | android.telephony.TelephonyManager | [public] | getNetworkType |
| 18 | android.telephony.TelephonyManager | [public] | getPhoneType |
| 19 | android.telephony.TelephonyManager | [public] | getSimCountryIso |
| 20 | android.telephony.TelephonyManager | [public] | getSimOperator |
| 21 | android.telephony.TelephonyManager | [public] | getSimOperatorName |
| 22 | android.telephony.TelephonyManager | [public] | getSimSerialNumber |
| 23 | android.telephony.TelephonyManager | [public] | getSimState |
| 24 | android.telephony.TelephonyManager | [public] | getSubscriberId |
| 25 | android.telephony.TelephonyManager | [public] | getVoiceMailAlphaTag |
| 26 | android.telephony.TelephonyManager | [public] | getVoiceMailNumber |
| 27 | android.telephony.TelephonyManager | [public] | hasCarrierPrivileges |
| 28 | android.telephony.TelephonyManager | [public] | hasIccCard |
| 29 | android.telephony.TelephonyManager | [public] | iccCloseLogicalChannel |
| 30 | android.telephony.TelephonyManager | [public] | iccExchangeSimIO |
| 31 | android.telephony.TelephonyManager | [public] | iccOpenLogicalChannel |
| 32 | android.telephony.TelephonyManager | [public] | iccTransmitApduBasicChannel |
| 33 | android.telephony.TelephonyManager | [public] | iccTransmitApduLogicalChannel |
| 34 | android.telephony.TelephonyManager | [public] | isNetworkRoaming |
| 35 | android.telephony.TelephonyManager | [public] | isSmsCapable |
| 36 | android.telephony.TelephonyManager | [public] | isVoiceCapable |
| 37 | android.telephony.TelephonyManager | [public] | listen |
| 38 | android.telephony.TelephonyManager | [public] | sendEnvelopeWithStatus |
| 39 | android.telephony.TelephonyManager | [public] | setLine1NumberForDisplay |
| 40 | android.telephony.TelephonyManager | [public] | setOperatorBrandOverride |
| 41 | android.telephony.TelephonyManager | [public] | setPreferredNetworkTypeToGlobal |
| 42 | android.telephony.TelephonyManager | [public] | setVoiceMailNumber |
+---------------------------------------------------------------------------------------------------------------------------+
project.find.nocase().calls.getter("calleed.name:login").show()
Sometimes there is too much matches and you would like filter the results by some characteristics like a AND clause into the last search request.
> projet.find.()
First activate hooks for methods:
> var meth = Project.find.method("enclosingClass.name:TelephonyManager").get(7)
> var hook = Project.hook.probe(meth);
Then, you can spawn/attach to appp with the new hook.
> Project.hook.startBySpawn( null, "com.myapp")
> project.security.*
This feature use the Search API in order to identified security mechanism and sort it by type.
If the device is connected, each identified mechanisms can be hooked or patched automatically by following a predefined pattern, or can be attacked manually.
All tests can be invoked in one way style by the method scan()
> project.security.scan()
<type_of_node>("<condition>") [.<filter>("<condition | value>")] *
.filter(<FinderResult>)
.ifilter(<FinderResult>)
.get(<FinderResult>)
.count(<FinderResult>)
.select(<FinderResult>)
.intersect(<FinderResult>)
Returns : FinderJoin
.on(<search_pattern>)
If the device are not connected while project initializing, don't worry ! Just plug it, and recall your last failed command. The device will be automatically detected and set as default device.
In this case you could show something like it before your command output :
> project.pull()
[!] Warning ! : device not selected. Searching ...
╔═══════════════════════════[ Android devices ]═══════════════════════════════╗
║ G3AZAAAAAA02A3B :ASUS_Z00AD ║
╚═════════════════════════════════════════════════════════════════════════════╝
[*] Device selected : G3AZAAAAAA02A3B
[*] Package found
[*] Package downloaded to /tmp/com.whatsapp.apk
[*] APK decompiled in /tmp/ws/com.whatsapp_dex
It appears when Dexcalibur does the static analysis of the target application, and solves references to Class/Field/Method/String. Depending of the application size (quantity of call, instruction, system method inherited, ...). Use the array in order to find good parameter
App sector | classes | method | call | instruction | thread | memory required |
---|---|---|---|---|---|---|
Messaging | 10423 | 49017 | 265 404 | 1 084 284 | 4 | 8192 |
Messaging | 28070 | 2 070 000 | 4 | 8192 | ||
Transport | 8630 | 46163 | 133643 | 630 732 | 4 | 4096 |
Bank | 11000 | 1 000 000 | 4 | 8192 |
Node.JS can be launch with V8 engine's custom parameters (choose value in the) :
:~$ node --v8-pool-size=<thread> --max-old-space-size=<memory>