├── .gitignore ├── README.md └── patch.sh /.gitignore: -------------------------------------------------------------------------------- 1 | extract_* 2 | *.apk 3 | *.keystore 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tconnectpatcher 2 | 3 | **December 2021 Update:** tconnectpatcher is now able to work around a Tandem bug which prevented v1.2 of the app from launching. 4 | Since tconnectpatcher has only supported app version 1.2, this allows quicker uploads in the t:connect app to be performed again. 5 | 6 | ## Prerequisites 7 | 8 | Tconnectpatcher patches the t:connect Android app to upload data via their API more frequently. 9 | It is written in bash, and should work on any Linux/Unix/MacOS system. 10 | In order to run the tool, you must have Python 3 and [apktool](https://ibotpeaches.github.io/Apktool/install/) installed. 11 | 12 | To generate a "patched" APK, a valid APK of the [t:connect Android application](https://play.google.com/store/apps/details?id=com.tandemdiabetes.tconnect) must be provided. 13 | Currently only version 1.2, released in September 2020, is supported. 14 | You can extract this APK from an Android phone which has the application installed from the Play Store, [or find it on the internet](http://google.com/search?q=com.tandemdiabetes.tconnect+1.2+android+apk) via one of the standard APK mirrors. Make sure to download version 1.2. 15 | 16 | Once you have the APK downloaded, open a Terminal and clone and cd into this repository. Or, download the `patch.sh` file (click on the file name and then the 'Raw' button) and cd to the folder where you downloaded that file. 17 | 18 | ```bash 19 | $ git clone https://github.com/jwoglom/tconnectpatcher 20 | $ cd tconnectpatcher 21 | $ ./patch.sh path/to/downloaded.apk 22 | ``` 23 | Then run `./patch.sh path/to/downloaded.apk`, where `path/to/downloaded.apk` is the path of the file you extraced or downloaded. (Hint: You can drag the .apk file into the Terminal window to get the full path.) Alternatively, move the downloaded APK into the same `tconnectpatcher` directory as the `patch.sh` file, and then run `./patch.sh ./downloaded.apk`, replacing `downloaded.apk` with the name of the file. 24 | 25 | You'll be asked: 26 | 27 | * **How often (in minutes) should t:connect upload data to the cloud?** If you'd like to upload your pump data more frequently to the cloud, set this to, e.g., `5` or `15` 28 | 29 | From there, follow the steps to create a local debug keystore, build the APK, and then install it on your phone. 30 | 31 | ## Advanced Usage 32 | 33 | To modify these options, run the tool with `./patch.sh path/to/downloaded.apk --advanced` 34 | 35 | You'll be prompted about several options: 36 | 37 | * **Would you like to make the APK debuggable?** This makes the Android app [debuggable](https://developer.android.com/guide/topics/manifest/application-element#debug). Defaults to yes. 38 | * **Would you like to disable certificate verification?** If set, this allows you to look at outgoing HTTPS connections made by the app. Defaults to yes. 39 | * **How often (in minutes) should t:connect upload data to the cloud?** If you'd like to upload your pump data more frequently to the cloud, set this to, e.g., `5` or `15` 40 | * **Modify to log bluetooth data?** If set, all bluetooth reads and writes are logged to `adb logcat` with tags `BLEREAD` and `BLEWRITE`. For use in logging phone-pump Bluetooth communication. Defaults to true. 41 | 42 | ## Example 43 | 44 | Here is an example invocation of the tool: 45 | 46 | ``` 47 | $ ./patch.sh tconnect_mobile_v1.2.apk 48 | t:connect Patcher: version 1.2 49 | github.com/jwoglom/tconnectpatcher 50 | ------------------------------------ 51 | 52 | This is a patcher utility which adds additional configuration 53 | options to the Tandem Diabetes t:connect Android app. 54 | 55 | Currently only version 1.2 is supported. 56 | 57 | Input APK: tconnect_mobile_v1.2.apk 58 | 59 | -------------------- PATCH OPTIONS -------------------- 60 | Would you like to make the APK debuggable? [Y/n] y 61 | Okay, making the APK debuggable. 62 | 63 | Would you like to disable certificate verification? [y/N] n 64 | Not updating the APK security configuration. 65 | 66 | How often (in minutes) should t:connect upload data to the cloud? [default: 60] 5 67 | Okay, will change the data upload rate to 5 minutes 68 | 69 | Beginning patch... 70 | Extracting APK to extract_tconnect_mobile_v1.2 71 | I: Using Apktool 2.4.1 on tconnect_mobile_v1.2.apk 72 | I: Loading resource table... 73 | I: Decoding AndroidManifest.xml with resources... 74 | I: Loading resource table from file: /Users/james/Library/apktool/framework/1.apk 75 | I: Regular manifest package... 76 | I: Decoding file-resources... 77 | I: Decoding values */* XMLs... 78 | I: Baksmaling classes.dex... 79 | I: Baksmaling classes2.dex... 80 | I: Copying assets and libs... 81 | I: Copying unknown files... 82 | I: Copying original files... 83 | I: Copying META-INF/services directory 84 | Package ID matches 85 | Applying APK modifications... 86 | Applying AndroidManifest patches 87 | Applying patches: 'android:extractNativeLibs': 'true', 'android:allowBackup': 'true', 'android:debuggable': 'true', 88 | Patching application tag in extract_tconnect_mobile_v1.2/AndroidManifest.xml 89 | Deleting field for replacement: {http://schemas.android.com/apk/res/android}extractNativeLibs 90 | Deleting field for replacement: {http://schemas.android.com/apk/res/android}allowBackup 91 | Applying data upload rate patch 92 | Replaced 0x36ee80 with 0x493e0 93 | Done patching source files. You can make any other modifications now. 94 | 95 | Continue to generate debug APK? [Y/n] y 96 | Okay. continuing. 97 | Generating debug APK tconnect_mobile_v1.2-debug.apk 98 | I: Using Apktool 2.4.1 99 | I: Checking whether sources has changed... 100 | I: Smaling smali folder into classes.dex... 101 | I: Checking whether sources has changed... 102 | I: Smaling smali_classes2 folder into classes2.dex... 103 | I: Checking whether resources has changed... 104 | I: Building resources... 105 | [ .. ] 106 | I: Built apk... 107 | Generating debug keystore at debug.keystore 108 | When prompted, enter any password. You will be prompted to re-enter it when signing the APK. 109 | All non-password fields are optional. Type 'yes' when asked if your input was correct. 110 | Enter keystore password: 111 | Re-enter new password: 112 | What is your first and last name? 113 | [Unknown]: 114 | What is the name of your organizational unit? 115 | [Unknown]: 116 | What is the name of your organization? 117 | [Unknown]: 118 | What is the name of your City or Locality? 119 | [Unknown]: 120 | What is the name of your State or Province? 121 | [Unknown]: 122 | What is the two-letter country code for this unit? 123 | [Unknown]: 124 | Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct? 125 | [no]: yes 126 | 127 | Generating 2,048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 10,000 days 128 | for: CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown 129 | [Storing debug.keystore] 130 | Signing debug APK... 131 | When prompted, enter the password for debug.keystore 132 | Enter Passphrase for keystore: 133 | [ .. ] 134 | jar signed. 135 | 136 | Warning: 137 | The signer's certificate is self-signed. 138 | No -tsa or -tsacert is provided and this jar is not timestamped. Without a timestamp, users may not be able to validate this jar after the signer certificate's expiration date (2048-07-31). 139 | Done! You can now install tconnect_mobile_v1.2-debug.apk on your device. 140 | 141 | IMPORTANT: If you currently have a release version of the t:connect app installed, 142 | you MUST uninstall it before installing the patched version. 143 | 144 | Would you like to install the patched APK now? [y/N] n 145 | You'll need to re-login to the app, and also un-pair and re-pair your pump to your phone. 146 | Thanks for using this tool! 147 | ``` 148 | 149 | After running the tool, you can install the APK on your Android device using `adb install .apk`. 150 | -------------------------------------------------------------------------------- /patch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | FAILED_PREREQS=0 4 | if [[ "$(which python3)" == "" ]]; then 5 | echo "ERROR: You do not have python3 installed." 1>&2; 6 | FAILED_PREREQS=1 7 | else 8 | (python3 -c "import xml.etree.ElementTree" > /dev/null 2>&1) || { 9 | echo "ERROR: Could not import xml Python 3 module." 1>&2; 10 | FAILED_PREREQS=1 11 | } 12 | fi 13 | 14 | if [[ "$(which apktool)" == "" ]]; then 15 | echo "ERROR: apktool is not in your PATH." 1>&2; 16 | echo "Please view the installation instructions: https://ibotpeaches.github.io/Apktool/install/" 1>&2; 17 | FAILED_PREREQS=1 18 | fi 19 | 20 | if [[ "$(which keytool)" == "" ]]; then 21 | echo "ERROR: keytool is not in your PATH." 1>&2; 22 | echo "Please ensure that apktool is fully installed: https://ibotpeaches.github.io/Apktool/install/" 1>&2; 23 | FAILED_PREREQS=1 24 | fi 25 | 26 | if [[ "$(which jarsigner)" == "" ]]; then 27 | echo "ERROR: jarsigner is not in your PATH." 1>&2; 28 | echo "Please ensure that apktool is fully installed: https://ibotpeaches.github.io/Apktool/install/" 1>&2; 29 | fi 30 | 31 | if [[ "$FAILED_PREREQS" == "1" ]]; then 32 | echo "Failed prereqs. Exiting." 33 | exit -1 34 | fi 35 | 36 | EXPECTED_PACKAGE=com.tandemdiabetes.tconnect 37 | APK_VERSION_1_2="1.2" 38 | APK_VERSION_1_4="1.4 (c8)" 39 | APK_VERSION_1_6="1.6 (11a)" 40 | EXPECTED_APK_VERSIONS=("$APK_VERSION_1_2") # "$APK_VERSION_1_4" "$APK_VERSION_1_6") 41 | 42 | echo " t:connect Patcher: version 1.5 " 43 | echo " github.com/jwoglom/tconnectpatcher " 44 | echo "------------------------------------" 45 | echo "" 46 | echo "This is a patcher utility which adds additional configuration" 47 | echo "options to the Tandem Diabetes t:connect Android app." 48 | echo "" 49 | echo "Currently only the following versions are supported:" 50 | for v in "${EXPECTED_APK_VERSIONS[@]}"; do 51 | echo " - $v" 52 | done 53 | echo "" 54 | 55 | INPUT_APK=$1 56 | if [ "$INPUT_APK" == "" ]; then 57 | echo "ERROR: Please specify an APK." 1>&2; 58 | echo "This APK should be $EXPECTED_PACKAGE" 59 | exit -1 60 | fi 61 | 62 | ADVANCED=n 63 | if [[ "$2" == "--advanced" ]]; then 64 | ADVANCED=y 65 | fi 66 | 67 | echo "Input APK: $INPUT_APK" 68 | echo "" 69 | echo "-------------------- PATCH OPTIONS --------------------" 70 | 71 | 72 | PATCH_DEBUGGABLE=y 73 | PATCH_SECURITY_CONFIG=y 74 | PATCH_BT_LOGGING=y 75 | if [[ "$ADVANCED" == "y" ]]; then 76 | read -p " Would you like to make the APK debuggable? [Y/n] " PATCH_DEBUGGABLE 77 | if [[ "$PATCH_DEBUGGABLE" == "y" || "$PATCH_DEBUGGABLE" == "Y" || "$PATCH_DEBUGGABLE" == "" ]]; then 78 | echo "Okay, making the APK debuggable." 79 | PATCH_DEBUGGABLE=y 80 | else 81 | echo "Not making the APK debuggable." 82 | fi 83 | echo "" 84 | 85 | 86 | read -p " Would you like to disable certificate verification? [y/N] " PATCH_SECURITY_CONFIG 87 | if [[ "$PATCH_SECURITY_CONFIG" == "y" || "$PATCH_SECURITY_CONFIG" == "Y" ]]; then 88 | echo "Okay, updating the APK security configuration." 89 | PATCH_SECURITY_CONFIG=y 90 | else 91 | echo "Not updating the APK security configuration." 92 | fi 93 | echo "" 94 | 95 | 96 | read -p " Modify to log bluetooth data? [y/N] " PATCH_BT_LOGGING 97 | if [[ "$PATCH_BT_LOGGING" == "y" || "$PATCH_BT_LOGGING" == "Y" ]]; then 98 | echo "Okay, will patch bluetooth logging." 99 | PATCH_BT_LOGGING=y 100 | else 101 | echo "Not patching bluetooth logging." 102 | fi 103 | echo "" 104 | fi 105 | 106 | read -p " How often (in minutes) should t:connect upload data to the cloud? [default: 60] " PATCH_UPLOAD_MINS 107 | if [[ "$PATCH_UPLOAD_MINS" == "60" ]]; then 108 | echo "Will not update data upload rate." 109 | elif [[ "$PATCH_UPLOAD_MINS" == "" ]]; then 110 | PATCH_UPLOAD_MINS=60 111 | echo "Will not update data upload rate." 112 | else 113 | echo "Okay, will change the data upload rate to $PATCH_UPLOAD_MINS minutes" 114 | fi 115 | echo "" 116 | 117 | 118 | echo "Beginning patch..." 119 | 120 | EXTRACT_FOLDER="extract_$(basename $INPUT_APK | sed 's/.apk//')" 121 | PATCHED_APK=$(basename $INPUT_APK | sed 's/.apk/-patched.apk/') 122 | 123 | echo "Extracting APK to $EXTRACT_FOLDER" 124 | DO_EXTRACT=y 125 | if [ -d "$EXTRACT_FOLDER" ]; then 126 | echo "Folder $EXTRACT_FOLDER already exists." 127 | read -p " Do you want to delete it and re-extract? [y/N] " DO_EXTRACT 128 | if [[ "$DO_EXTRACT" == "y" || "$DO_EXTRACT" == "Y" ]]; then 129 | echo "Deleting $EXTRACT_FOLDER" 130 | rm -rf "$EXTRACT_FOLDER" 131 | DO_EXTRACT=y 132 | else 133 | echo "Okay, will use the already extracted folder." 134 | DO_EXTRACT=n 135 | fi 136 | fi 137 | 138 | 139 | if [[ "$DO_EXTRACT" == "y" ]]; then 140 | apktool d --use-aapt2 -o "$EXTRACT_FOLDER" "$INPUT_APK" 141 | fi 142 | 143 | MANIFEST_XML=$EXTRACT_FOLDER/AndroidManifest.xml 144 | python3 -c " 145 | import sys 146 | import xml.etree.ElementTree as etree 147 | schema = 'http://schemas.android.com/apk/res/android' 148 | etree.register_namespace('android', schema) 149 | et = etree.parse('$MANIFEST_XML') 150 | root = et.getroot() 151 | if root.attrib['package'].lower() != '$EXPECTED_PACKAGE'.lower(): 152 | print('Found package ID:', root.attrib['package']) 153 | sys.exit(-1) 154 | else: 155 | print('Package ID matches') 156 | sys.exit(0) 157 | " || { 158 | echo "" 159 | echo "ERROR: The APK provided has an unexpected package name. The patcher will not work properly." 1>&2; 160 | echo "Please pass an APK with package name $EXPECTED_PACKAGE to this tool." 1>&2; 161 | exit -1 162 | } 163 | 164 | APK_VERSION=$(grep 'versionName:' $EXTRACT_FOLDER/apktool.yml | sed "s/\(.*\)\: \(.*\)/\2/" | tr -d '"' | tr -d "'") 165 | 166 | version_ok=false 167 | for v in "${EXPECTED_APK_VERSIONS[@]}"; do 168 | if [[ "$APK_VERSION" == "$v" ]]; then 169 | version_ok=true 170 | fi 171 | done 172 | 173 | if [[ "$version_ok" == "false" ]]; then 174 | echo "" 175 | echo "WARNING: The APK provided has an unexpected version. The patcher may not work properly." 1>&2; 176 | echo "Found version: $APK_VERSION" 1>&2; 177 | echo "Expected versions: ${EXPECTED_APK_VERSIONS[@]}" 1>&2; 178 | echo "" 179 | else 180 | echo "Found APK version $APK_VERSION" 181 | fi 182 | 183 | 184 | 185 | echo "Applying APK modifications..." 186 | 187 | echo "Applying AndroidManifest patches" 188 | 189 | # Fix 'Failure [INSTALL_FAILED_INVALID_APK: Failed to extract native libraries, res=-2]' 190 | # see https://github.com/iBotPeaches/Apktool/issues/1626 191 | ATTRIB_PATCHES="'android:extractNativeLibs': 'true', " 192 | 193 | if [[ "$PATCH_DEBUGGABLE" == "y" ]]; then 194 | ATTRIB_PATCHES="${ATTRIB_PATCHES}'android:allowBackup': 'true', 'android:debuggable': 'true', " 195 | fi 196 | 197 | if [[ "$PATCH_SECURITY_CONFIG" == "y" ]]; then 198 | ATTRIB_PATCHES="${ATTRIB_PATCHES}'android:networkSecurityConfig': '@xml/network_security_config', " 199 | fi 200 | 201 | echo "Applying patches: $ATTRIB_PATCHES" 202 | 203 | python3 -c " 204 | import xml.etree.ElementTree as etree 205 | schema = 'http://schemas.android.com/apk/res/android' 206 | etree.register_namespace('android', schema) 207 | et = etree.parse('$MANIFEST_XML') 208 | for child in et.getroot(): 209 | if child.tag == 'application': 210 | print('Patching application tag in $MANIFEST_XML') 211 | patches = { 212 | $ATTRIB_PATCHES 213 | } 214 | for field in patches.keys(): 215 | rawfield = field.replace('android:', '{' + schema + '}') 216 | if rawfield in child.attrib.keys(): 217 | print('Deleting field for replacement:', rawfield) 218 | del child.attrib[rawfield] 219 | child.attrib.update(patches) 220 | et.write('$MANIFEST_XML' + '_new', encoding='utf-8', xml_declaration=True) 221 | " 222 | if [ ! -f "${MANIFEST_XML}_new" ]; then 223 | echo "ERROR: Updated manifest file was not generated. Exiting." 224 | exit -1 225 | fi 226 | mv ${MANIFEST_XML}_new $MANIFEST_XML 227 | 228 | 229 | if [[ "$PATCH_SECURITY_CONFIG" == "y" ]]; then 230 | echo "Adding network_security_config" 231 | 232 | RES_XML_FOLDER=$EXTRACT_FOLDER/res/xml 233 | if [ ! -f "$RES_XML_FOLDER" ]; then 234 | mkdir -p "$RES_XML_FOLDER" 235 | fi 236 | 237 | NETWORK_SECURITY_CONFIG=$RES_XML_FOLDER/network_security_config.xml 238 | 239 | cat < $NETWORK_SECURITY_CONFIG 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | EOF 250 | fi 251 | 252 | if [[ "$PATCH_UPLOAD_MINS" != "60" ]]; then 253 | echo "Applying data upload rate patch" 254 | 255 | if [[ "$APK_VERSION" == "$APK_VERSION_1_2" ]]; then 256 | # com.tandemdiabetes.tconnect.p088a.AppComponentStore 257 | APP_COMPONENT_STORE_SMALI=$EXTRACT_FOLDER/smali/com/tandemdiabetes/tconnect/a/a.smali 258 | # private final long OneHourInMillis = 3600000 259 | OLD_INSTRUCTION_PREFIX="const-wide/32 p1," 260 | OLD_INSTRUCTION_VALUE="0x36ee80" # 3600000 261 | 262 | python3 -c " 263 | orig = open('$APP_COMPONENT_STORE_SMALI').read() 264 | old = '$OLD_INSTRUCTION_PREFIX $OLD_INSTRUCTION_VALUE' 265 | if old in orig: 266 | val = hex(int($PATCH_UPLOAD_MINS) * 60 * 1000) 267 | orig = orig.replace(old, '$OLD_INSTRUCTION_PREFIX ' + str(val)) 268 | print('Replaced $OLD_INSTRUCTION_VALUE with ' + str(val)) 269 | open('$APP_COMPONENT_STORE_SMALI', 'w').write(orig) 270 | else: 271 | print('Could not find '+old+' in $APP_COMPONENT_STORE_SMALI -- it may have already been patched.') 272 | " 273 | elif [[ "$APK_VERSION" == "$APK_VERSION_1_4" ]]; then 274 | # AppComponentStore.kt Runnable 275 | APP_COMPONENT_STORE_RUNNABLE_SMALI=$EXTRACT_FOLDER/smali_classes2/com/tandemdiabetes/tconnect/a/a\$d.smali 276 | # new PeriodicWorkRequest.Builder(PeriodicUploadTriggerWorker.class, 60, TimeUnit.MINUTES) 277 | OLD_INSTRUCTION_PREFIX="const-wide/16 v5," 278 | OLD_INSTRUCTION_VALUE="0x3c" # 60 279 | 280 | python3 -c " 281 | orig = open('$APP_COMPONENT_STORE_RUNNABLE_SMALI').read() 282 | old = '$OLD_INSTRUCTION_PREFIX $OLD_INSTRUCTION_VALUE' 283 | if old in orig: 284 | val = hex(int($PATCH_UPLOAD_MINS)) 285 | orig = orig.replace(old, '$OLD_INSTRUCTION_PREFIX ' + str(val)) 286 | print('Replaced $OLD_INSTRUCTION_VALUE with ' + str(val)) 287 | open('$APP_COMPONENT_STORE_RUNNABLE_SMALI', 'w').write(orig) 288 | else: 289 | print('Could not find '+old+' in $APP_COMPONENT_STORE_RUNNABLE_SMALI -- it may have already been patched.') 290 | " 291 | elif [[ "$APK_VERSION" == "$APK_VERSION_1_6" ]]; then 292 | # AppComponentStore.kt Runnable 293 | APP_COMPONENT_STORE_RUNNABLE_SMALI=$EXTRACT_FOLDER/smali_classes2/com/tandemdiabetes/tconnect/a/a\$d.smali 294 | # new PeriodicWorkRequest.Builder(PeriodicUploadTriggerWorker.class, 60, TimeUnit.MINUTES) 295 | OLD_INSTRUCTION_PREFIX="const-wide/16 v5," 296 | OLD_INSTRUCTION_VALUE="0x3c" # 60 297 | 298 | python3 -c " 299 | orig = open('$APP_COMPONENT_STORE_RUNNABLE_SMALI').read() 300 | old = '$OLD_INSTRUCTION_PREFIX $OLD_INSTRUCTION_VALUE' 301 | if old in orig: 302 | val = hex(int($PATCH_UPLOAD_MINS)) 303 | orig = orig.replace(old, '$OLD_INSTRUCTION_PREFIX ' + str(val)) 304 | print('Replaced $OLD_INSTRUCTION_VALUE with ' + str(val)) 305 | open('$APP_COMPONENT_STORE_RUNNABLE_SMALI', 'w').write(orig) 306 | else: 307 | print('Could not find '+old+' in $APP_COMPONENT_STORE_RUNNABLE_SMALI -- it may have already been patched.') 308 | " 309 | else 310 | echo "ERROR: unable to perform upload patch on this version." 311 | exit -1 312 | fi 313 | fi 314 | 315 | # This doesn't allow the app to launch. 316 | if [[ "$APK_VERSION" == "$APK_VERSION_1_4" ]]; then 317 | echo "Nullifying MessageGuardException..." 318 | 319 | PROTECTED_APP_SMALI=$EXTRACT_FOLDER/smali/com/tandemdiabetes/tconnect/ProtectedApp.smali 320 | 321 | MESSAGE_GUARD_INVOCATION_1='invoke-direct {v1, v2, v0}, Lcom/tandemdiabetes/tconnect/MessageGuardException;->(Ljava/lang/String;Ljava/lang/Throwable;)V\n\n throw v1' 322 | MESSAGE_GUARD_INVOCATION_2='invoke-direct {v4, v5, v1}, Lcom/tandemdiabetes/tconnect/MessageGuardException;->(Ljava/lang/String;Ljava/lang/Throwable;)V\n\n throw v4' 323 | python3 -c " 324 | orig = open('$PROTECTED_APP_SMALI').read() 325 | a = '$MESSAGE_GUARD_INVOCATION_1' 326 | b = '$MESSAGE_GUARD_INVOCATION_2' 327 | if a in orig and b in orig: 328 | orig = orig.replace(a, 'return-void') 329 | orig = orig.replace(b, 'return-void') 330 | print('Removed '+a) 331 | print('Removed '+b) 332 | 333 | open('$PROTECTED_APP_SMALI', 'w').write(orig) 334 | else: 335 | print('Could not find '+a+' or '+b+' in $PROTECTED_APP_SMALI -- it may have already been patched.') 336 | " 337 | fi 338 | 339 | # This doesn't allow the app to launch. 340 | if [[ "$APK_VERSION" == "$APK_VERSION_1_6" ]]; then 341 | echo "Nullifying MessageGuardException..." 342 | 343 | PROTECTED_APP_SMALI=$EXTRACT_FOLDER/smali/com/tandemdiabetes/tconnect/ProtectedApp.smali 344 | 345 | MESSAGE_GUARD_INVOCATION_1='new-instance v0, Lcom/tandemdiabetes/tconnect/MessageGuardException;' 346 | MESSAGE_GUARD_INVOCATION_2='sget-object v1, Lcom/tandemdiabetes/tconnect/ProtectedApp;->dD:Ljava/lang/String;' 347 | MESSAGE_GUARD_INVOCATION_3='invoke-direct {v0, p1, v1}, Lcom/tandemdiabetes/tconnect/MessageGuardException;->(Ljava/lang/Throwable;Ljava/lang/String;)V' 348 | MESSAGE_GUARD_THROW='throw v0' 349 | MESSAGE_GUARD_NATIVE_1='.method private static native jrthH([I)V' 350 | MESSAGE_GUARD_NATIVE_1_REPL='.method private static jrthH([I)V\nreturn-void' 351 | python3 -c " 352 | orig = open('$PROTECTED_APP_SMALI').read() 353 | a = '$MESSAGE_GUARD_INVOCATION_1' 354 | b = '$MESSAGE_GUARD_INVOCATION_2' 355 | c = '$MESSAGE_GUARD_INVOCATION_3' 356 | d = '$MESSAGE_GUARD_THROW' 357 | native1 = '$MESSAGE_GUARD_NATIVE_1' 358 | native1_repl = '$MESSAGE_GUARD_NATIVE_1_REPL' 359 | if a in orig and b in orig and c in orig and d in orig: 360 | orig = orig.replace(a, '') 361 | orig = orig.replace(b, '') 362 | orig = orig.replace(c, '') 363 | orig = orig.replace(d, 'return-void') 364 | orig = orig.replace(native1, native1_repl) 365 | print('Removed '+a) 366 | print('Removed '+b) 367 | print('Removed '+c) 368 | print('Replaced '+c) 369 | 370 | open('$PROTECTED_APP_SMALI', 'w').write(orig) 371 | else: 372 | print('Could not find required strings in $PROTECTED_APP_SMALI -- it may have already been patched.') 373 | " 374 | fi 375 | 376 | # APK version 1.2 no longer signs in. Remove certificate verification from Volley.newRequestQueue 377 | if [[ "$APK_VERSION" == "$APK_VERSION_1_2" ]]; then 378 | VOLLEY_BEFORE='invoke-static {v0, v2}, Lcom/android/volley/toolbox/Volley;->newRequestQueue(Landroid/content/Context;Lcom/android/volley/toolbox/BaseHttpStack;)Lcom/android/volley/RequestQueue;' 379 | VOLLEY_AFTER='invoke-static {v0}, Lcom/android/volley/toolbox/Volley;->newRequestQueue(Landroid/content/Context;)Lcom/android/volley/RequestQueue;' 380 | 381 | NETWORK_SMALI=$EXTRACT_FOLDER/smali/com/tandemdiabetes/network/a.smali 382 | python3 -c " 383 | orig = open('$NETWORK_SMALI').read() 384 | before = '$VOLLEY_BEFORE' 385 | after = '$VOLLEY_AFTER' 386 | 387 | if before in orig: 388 | orig = orig.replace(before, after) 389 | print('Patched Volley.newRequestQueue') 390 | 391 | open('$NETWORK_SMALI', 'w').write(orig) 392 | else: 393 | print('Could not find required strings in $NETWORK_SMALI -- it may have already been patched.') 394 | " 395 | 396 | fi 397 | 398 | if [[ "$PATCH_BT_LOGGING" == "y" ]]; then 399 | if [[ "$APK_VERSION" == "$APK_VERSION_1_2" ]]; then 400 | 401 | echo "Adding Bluetooth read logging" 402 | 403 | BT_LOGGING_READ_BEFORE='invoke-virtual {v1, v2}, Lcom/tandemdiabetes/ble/i/h;->a([B)V' 404 | BT_LOGGING_READ_CHECKSTRING='const-string v4, "BLEREAD"' 405 | BT_LOGGING_READ_ADDED='invoke-static {v2}, Lorg/apache/commons/codec/binary/Hex;->encodeHexString([B)Ljava/lang/String; 406 | move-result-object v5 407 | const-string v4, "BLEREAD" 408 | invoke-static {v4, v5}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I' 409 | CONTROLLER_READ_SMALI=$EXTRACT_FOLDER/smali/com/tandemdiabetes/ble/daemon/'Controller$f.smali' 410 | 411 | python3 -c " 412 | orig = open('$CONTROLLER_READ_SMALI').read() 413 | before = '$BT_LOGGING_READ_BEFORE' 414 | checkstring = '$BT_LOGGING_READ_CHECKSTRING' 415 | after = '''$BT_LOGGING_READ_BEFORE 416 | $BT_LOGGING_READ_ADDED''' 417 | 418 | if before in orig and not checkstring in orig: 419 | orig = orig.replace(before, after) 420 | print('Added BT read logging') 421 | 422 | open('$CONTROLLER_READ_SMALI', 'w').write(orig) 423 | else: 424 | print('Could not find required strings in $CONTROLLER_READ_SMALI -- it may have already been patched.') 425 | " 426 | echo "Adding Bluetooth write logging" 427 | 428 | BT_LOGGING_WRITE_BEFORE='invoke-virtual {p0, v0}, Lcom/tandemdiabetes/ble/daemon/Controller;->a(Lcom/tandemdiabetes/ble/daemon/g;)V' 429 | BT_LOGGING_WRITE_CHECKSTRING='const-string v3, "BLEWRITE"' 430 | BT_LOGGING_WRITE_ADDED='invoke-static {p1}, Lorg/apache/commons/codec/binary/Hex;->encodeHexString([B)Ljava/lang/String; 431 | move-result-object v4 432 | const-string v3, "BLEWRITE" 433 | invoke-static {v3, v4}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I' 434 | CONTROLLER_WRITE_SMALI=$EXTRACT_FOLDER/smali/com/tandemdiabetes/ble/daemon/Controller.smali 435 | 436 | python3 -c " 437 | orig = open('$CONTROLLER_WRITE_SMALI').read() 438 | before = '$BT_LOGGING_WRITE_BEFORE' 439 | checkstring = '$BT_LOGGING_WRITE_CHECKSTRING' 440 | after = '''$BT_LOGGING_WRITE_ADDED 441 | $BT_LOGGING_WRITE_BEFORE 442 | ''' 443 | 444 | if before in orig and not checkstring in orig: 445 | orig = orig.replace(before, after) 446 | print('Added BT write logging') 447 | 448 | open('$CONTROLLER_WRITE_SMALI', 'w').write(orig) 449 | else: 450 | print('Could not find required strings in $CONTROLLER_WRITE_SMALI -- it may have already been patched.') 451 | " 452 | 453 | fi 454 | fi 455 | 456 | 457 | echo "Done patching source files. You can make any other modifications now." 458 | echo "" 459 | read -p " Continue to generate patched APK? [Y/n] " CONTINUE 460 | if [[ "$CONTINUE" == "y" || "$CONTINUE" == "Y" || "$CONTINUE" == "" ]]; then 461 | echo "Okay. continuing." 462 | else 463 | exit -1; 464 | fi 465 | 466 | echo "Generating patched APK $PATCHED_APK" 467 | apktool b --use-aapt2 -o "$PATCHED_APK" "$EXTRACT_FOLDER" 468 | 469 | if [[ ! -f "$PATCHED_APK" ]]; then 470 | echo "ERROR: apktool failed to generate a patched APK." 471 | echo "Check the error output above." 472 | exit -1 473 | fi 474 | 475 | KEYSTORE=debug.keystore 476 | 477 | if [[ "$KEYSTORE_TYPE" == "" ]]; then 478 | KEYSTORE_TYPE=SHA1 479 | fi 480 | 481 | if [ ! -f "$KEYSTORE" ]; then 482 | echo "Generating debug keystore at $KEYSTORE" 483 | echo " When prompted, enter any password. You will be prompted to re-enter it when signing the APK." 484 | echo " For simplicity, you can input the word 'password'" 485 | echo " All non-password fields are optional. Type 'yes' when asked if your input was correct." 486 | 487 | if [[ "$KEYSTORE_TYPE" == "SHA1" ]]; then 488 | keytool -genkey -v -keystore "$KEYSTORE" -alias alias_name -keyalg RSA -keysize 2048 -validity 10000 489 | elif [[ "$KEYSTORE_TYPE" == "MD5" ]]; then 490 | keytool -genkey -v -keystore "$KEYSTORE" -alias alias_name -sigalg MD5withRSA -keyalg RSA -keysize 1024 -validity 10000 491 | else 492 | echo "ERROR: unknown KEYSTORE_TYPE: $KEYSTORE_TYPE" 493 | exit -1 494 | fi 495 | else 496 | echo " Existing debug keystore found. Please enter your existing keystore password (e.g. 'password')" 497 | fi 498 | 499 | echo "Signing patched APK..." 500 | echo " When prompted, enter the password for $KEYSTORE" 501 | if [[ "$KEYSTORE_TYPE" == "SHA1" ]]; then 502 | jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore "$KEYSTORE" "$PATCHED_APK" alias_name 503 | elif [[ "$KEYSTORE_TYPE" == "MD5" ]]; then 504 | jarsigner -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore "$KEYSTORE" "$PATCHED_APK" alias_name 505 | else 506 | echo "ERROR: unknown KEYSTORE_TYPE: $KEYSTORE_TYPE" 507 | exit -1 508 | fi 509 | 510 | 511 | echo "Done! You can now install $PATCHED_APK on your device." 512 | echo "" 513 | echo "IMPORTANT: If you currently have a release version of the t:connect app installed," 514 | echo "you MUST uninstall it before installing the patched version. This tool will attempt" 515 | echo "to do so automatically." 516 | echo "" 517 | echo "Please plug in your Android device and enable USB debugging in settings." 518 | echo "" 519 | read -p " Would you like to install the patched APK now? [y/N] " INSTALL_NOW 520 | if [[ "$INSTALL_NOW" == "y" || "$INSTALL_NOW" == "Y" ]]; then 521 | echo "Okay, running adb install" 522 | if [[ "$(which python3)" == "" ]]; then 523 | echo "ERROR: You do not have adb installed, so cannot install the APK directly." 1>&2; 524 | echo "You can upload the apk file to your phone and install it manually." 1>&2; 525 | else 526 | tmpfile=$(mktemp) 527 | (adb shell dumpsys package "$EXPECTED_PACKAGE" 2>&1 | grep 'pkgFlags=') | tee $tmpfile 528 | if grep -q "DEBUGGABLE" $tmpfile; then 529 | echo "A patched t:connect application is already installed." 530 | else 531 | echo " You have a non-patched version of t:connect application already installed." 532 | read -p " Can it be uninstalled now? [Y/n]" UNINSTALL 533 | if [[ "$UNINSTALL" == "y" || "$UNINSTALL" == "Y" ]]; then 534 | adb uninstall com.tandemdiabetes.tconnect 535 | echo "OK, continuing to install patched APK" 536 | else 537 | echo "Warning, the application may not properly install" 538 | fi 539 | fi 540 | 541 | tmpfile=$(mktemp) 542 | (adb install $PATCHED_APK 2>&1 ) | tee $tmpfile 543 | if grep -q "INSTALL_FAILED_UPDATE_INCOMPATIBLE" $tmpfile; then 544 | read -p " The package was not installed. Would you like to delete the existing t:connect application and reinstall? [Y/n]" REINSTALL 545 | if [[ "$REINSTALL" == "y" || "$REINSTALL" == "Y" ]]; then 546 | adb uninstall com.tandemdiabetes.tconnect 547 | echo "Retrying install..." 548 | adb install $PATCHED_APK 549 | else 550 | exit 1 551 | fi 552 | fi 553 | 554 | echo "All set!" 555 | fi 556 | fi 557 | 558 | echo "" 559 | echo "You'll need to re-login to the app, and also un-pair and re-pair your pump to your phone." 560 | echo "Thanks for using this tool!" 561 | --------------------------------------------------------------------------------