Home | History | Annotate | Download | only in obbtool
      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