1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <androidfw/ObbFile.h> 18 #include <utils/String8.h> 19 20 #include <getopt.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 using namespace android; 26 27 static const char* gProgName = "obbtool"; 28 static const char* gProgVersion = "1.0"; 29 30 static int wantUsage = 0; 31 static int wantVersion = 0; 32 33 #define SALT_LEN 8 34 35 #define ADD_OPTS "n:v:os:" 36 static const struct option longopts[] = { 37 {"help", no_argument, &wantUsage, 1}, 38 {"version", no_argument, &wantVersion, 1}, 39 40 /* Args for "add" */ 41 {"name", required_argument, NULL, 'n'}, 42 {"version", required_argument, NULL, 'v'}, 43 {"overlay", optional_argument, NULL, 'o'}, 44 {"salt", required_argument, NULL, 's'}, 45 46 {NULL, 0, NULL, '\0'} 47 }; 48 49 class PackageInfo { 50 public: 51 PackageInfo() 52 : packageName(NULL) 53 , packageVersion(-1) 54 , overlay(false) 55 , salted(false) 56 { 57 memset(&salt, 0, sizeof(salt)); 58 } 59 60 char* packageName; 61 int packageVersion; 62 bool overlay; 63 bool salted; 64 unsigned char salt[SALT_LEN]; 65 }; 66 67 /* 68 * Print usage info. 69 */ 70 void usage(void) 71 { 72 fprintf(stderr, "Opaque Binary Blob (OBB) Tool\n\n"); 73 fprintf(stderr, "Usage:\n"); 74 fprintf(stderr, 75 " %s a[dd] [ OPTIONS ] FILENAME\n" 76 " Adds an OBB signature to the file.\n\n", gProgName); 77 fprintf(stderr, 78 " Options:\n" 79 " -n <package name> sets the OBB package name (required)\n" 80 " -v <OBB version> sets the OBB version (required)\n" 81 " -o sets the OBB overlay flag\n" 82 " -s <8 byte hex salt> sets the crypto key salt (if encrypted)\n" 83 "\n"); 84 fprintf(stderr, 85 " %s r[emove] FILENAME\n" 86 " Removes the OBB signature from the file.\n\n", gProgName); 87 fprintf(stderr, 88 " %s i[nfo] FILENAME\n" 89 " Prints the OBB signature information of a file.\n\n", gProgName); 90 } 91 92 void doAdd(const char* filename, struct PackageInfo* info) { 93 ObbFile *obb = new ObbFile(); 94 if (obb->readFrom(filename)) { 95 fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename); 96 return; 97 } 98 99 obb->setPackageName(String8(info->packageName)); 100 obb->setVersion(info->packageVersion); 101 obb->setOverlay(info->overlay); 102 if (info->salted) { 103 obb->setSalt(info->salt, SALT_LEN); 104 } 105 106 if (!obb->writeTo(filename)) { 107 fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n", 108 filename, strerror(errno)); 109 return; 110 } 111 112 fprintf(stderr, "OBB signature successfully written\n"); 113 } 114 115 void doRemove(const char* filename) { 116 ObbFile *obb = new ObbFile(); 117 if (!obb->readFrom(filename)) { 118 fprintf(stderr, "ERROR: %s: no OBB signature present\n", filename); 119 return; 120 } 121 122 if (!obb->removeFrom(filename)) { 123 fprintf(stderr, "ERROR: %s: couldn't remove OBB signature\n", filename); 124 return; 125 } 126 127 fprintf(stderr, "OBB signature successfully removed\n"); 128 } 129 130 void doInfo(const char* filename) { 131 ObbFile *obb = new ObbFile(); 132 if (!obb->readFrom(filename)) { 133 fprintf(stderr, "ERROR: %s: couldn't read OBB signature\n", filename); 134 return; 135 } 136 137 printf("OBB info for '%s':\n", filename); 138 printf("Package name: %s\n", obb->getPackageName().string()); 139 printf(" Version: %d\n", obb->getVersion()); 140 printf(" Flags: 0x%08x\n", obb->getFlags()); 141 printf(" Overlay: %s\n", obb->isOverlay() ? "true" : "false"); 142 printf(" Salt: "); 143 144 size_t saltLen; 145 const unsigned char* salt = obb->getSalt(&saltLen); 146 if (salt != NULL) { 147 for (int i = 0; i < SALT_LEN; i++) { 148 printf("%02x", salt[i]); 149 } 150 printf("\n"); 151 } else { 152 printf("<empty>\n"); 153 } 154 } 155 156 bool fromHex(char h, unsigned char *b) { 157 if (h >= '0' && h <= '9') { 158 *b = h - '0'; 159 return true; 160 } else if (h >= 'a' && h <= 'f') { 161 *b = h - 'a' + 10; 162 return true; 163 } else if (h >= 'A' && h <= 'F') { 164 *b = h - 'A' + 10; 165 return true; 166 } 167 return false; 168 } 169 170 bool hexToByte(char h1, char h2, unsigned char* b) { 171 unsigned char first, second; 172 if (!fromHex(h1, &first)) return false; 173 if (!fromHex(h2, &second)) return false; 174 *b = (first << 4) | second; 175 return true; 176 } 177 178 /* 179 * Parse args. 180 */ 181 int main(int argc, char* const argv[]) 182 { 183 int opt; 184 int option_index = 0; 185 struct PackageInfo package_info; 186 187 int result = 1; // pessimistically assume an error. 188 189 if (argc < 2) { 190 wantUsage = 1; 191 goto bail; 192 } 193 194 while ((opt = getopt_long(argc, argv, ADD_OPTS, longopts, &option_index)) != -1) { 195 switch (opt) { 196 case 0: 197 if (longopts[option_index].flag) 198 break; 199 fprintf(stderr, "'%s' requires an argument\n", longopts[option_index].name); 200 wantUsage = 1; 201 goto bail; 202 case 'n': 203 package_info.packageName = optarg; 204 break; 205 case 'v': { 206 char* end; 207 package_info.packageVersion = strtol(optarg, &end, 10); 208 if (*optarg == '\0' || *end != '\0') { 209 fprintf(stderr, "ERROR: invalid version; should be integer!\n\n"); 210 wantUsage = 1; 211 goto bail; 212 } 213 break; 214 } 215 case 'o': 216 package_info.overlay = true; 217 break; 218 case 's': 219 if (strlen(optarg) != SALT_LEN * 2) { 220 fprintf(stderr, "ERROR: salt must be 8 bytes in hex (e.g., ABCD65031337D00D)\n\n"); 221 wantUsage = 1; 222 goto bail; 223 } 224 225 package_info.salted = true; 226 227 unsigned char b; 228 for (int i = 0, j = 0; i < SALT_LEN; i++, j+=2) { 229 if (!hexToByte(optarg[j], optarg[j+1], &b)) { 230 fprintf(stderr, "ERROR: salt must be in hex (e.g., ABCD65031337D00D)\n"); 231 wantUsage = 1; 232 goto bail; 233 } 234 package_info.salt[i] = b; 235 } 236 break; 237 case '?': 238 wantUsage = 1; 239 goto bail; 240 } 241 } 242 243 if (wantVersion) { 244 fprintf(stderr, "%s %s\n", gProgName, gProgVersion); 245 } 246 247 if (wantUsage) { 248 goto bail; 249 } 250 251 #define CHECK_OP(name) \ 252 if (strncmp(op, name, opsize)) { \ 253 fprintf(stderr, "ERROR: unknown function '%s'!\n\n", op); \ 254 wantUsage = 1; \ 255 goto bail; \ 256 } 257 258 if (optind < argc) { 259 const char* op = argv[optind++]; 260 const int opsize = strlen(op); 261 262 if (optind >= argc) { 263 fprintf(stderr, "ERROR: filename required!\n\n"); 264 wantUsage = 1; 265 goto bail; 266 } 267 268 const char* filename = argv[optind++]; 269 270 switch (op[0]) { 271 case 'a': 272 CHECK_OP("add"); 273 if (package_info.packageName == NULL) { 274 fprintf(stderr, "ERROR: arguments required 'packageName' and 'version'\n"); 275 goto bail; 276 } 277 doAdd(filename, &package_info); 278 break; 279 case 'r': 280 CHECK_OP("remove"); 281 doRemove(filename); 282 break; 283 case 'i': 284 CHECK_OP("info"); 285 doInfo(filename); 286 break; 287 default: 288 fprintf(stderr, "ERROR: unknown command '%s'!\n\n", op); 289 wantUsage = 1; 290 goto bail; 291 } 292 } 293 294 bail: 295 if (wantUsage) { 296 usage(); 297 result = 2; 298 } 299 300 return result; 301 } 302