/* * Copyright (C) 2010 The Android Open Source Project * * 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. */ #include <androidfw/ObbFile.h> #include <utils/String8.h> #include <getopt.h> #include <stdio.h> #include <stdlib.h> #include <string.h> using namespace android; static const char* gProgName = "obbtool"; static const char* gProgVersion = "1.0"; static int wantUsage = 0; static int wantVersion = 0; #define SALT_LEN 8 #define ADD_OPTS "n:v:os:" static const struct option longopts[] = { {"help", no_argument, &wantUsage, 1}, {"version", no_argument, &wantVersion, 1}, /* Args for "add" */ {"name", required_argument, NULL, 'n'}, {"version", required_argument, NULL, 'v'}, {"overlay", optional_argument, NULL, 'o'}, {"salt", required_argument, NULL, 's'}, {NULL, 0, NULL, '\0'} }; class PackageInfo { public: PackageInfo() : packageName(NULL) , packageVersion(-1) , overlay(false) , salted(false) { memset(&salt, 0, sizeof(salt)); } char* packageName; int packageVersion; bool overlay; bool salted; unsigned char salt[SALT_LEN]; }; /* * Print usage info. */ void usage(void) { fprintf(stderr, "Opaque Binary Blob (OBB) Tool\n\n"); fprintf(stderr, "Usage:\n"); fprintf(stderr, " %s a[dd] [ OPTIONS ] FILENAME\n" " Adds an OBB signature to the file.\n\n", gProgName); fprintf(stderr, " Options:\n" " -n <package name> sets the OBB package name (required)\n" " -v <OBB version> sets the OBB version (required)\n" " -o sets the OBB overlay flag\n" " -s <8 byte hex salt> sets the crypto key salt (if encrypted)\n" "\n"); fprintf(stderr, " %s r[emove] FILENAME\n" " Removes the OBB signature from the file.\n\n", gProgName); fprintf(stderr, " %s i[nfo] FILENAME\n" " Prints the OBB signature information of a file.\n\n", gProgName); } void doAdd(const char* filename, struct PackageInfo* info) { ObbFile *obb = new ObbFile(); if (obb->readFrom(filename)) { fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename); return; } obb->setPackageName(String8(info->packageName)); obb->setVersion(info->packageVersion); obb->setOverlay(info->overlay); if (info->salted) { obb->setSalt(info->salt, SALT_LEN); } if (!obb->writeTo(filename)) { fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n", filename, strerror(errno)); return; } fprintf(stderr, "OBB signature successfully written\n"); } void doRemove(const char* filename) { ObbFile *obb = new ObbFile(); if (!obb->readFrom(filename)) { fprintf(stderr, "ERROR: %s: no OBB signature present\n", filename); return; } if (!obb->removeFrom(filename)) { fprintf(stderr, "ERROR: %s: couldn't remove OBB signature\n", filename); return; } fprintf(stderr, "OBB signature successfully removed\n"); } void doInfo(const char* filename) { ObbFile *obb = new ObbFile(); if (!obb->readFrom(filename)) { fprintf(stderr, "ERROR: %s: couldn't read OBB signature\n", filename); return; } printf("OBB info for '%s':\n", filename); printf("Package name: %s\n", obb->getPackageName().string()); printf(" Version: %d\n", obb->getVersion()); printf(" Flags: 0x%08x\n", obb->getFlags()); printf(" Overlay: %s\n", obb->isOverlay() ? "true" : "false"); printf(" Salt: "); size_t saltLen; const unsigned char* salt = obb->getSalt(&saltLen); if (salt != NULL) { for (int i = 0; i < SALT_LEN; i++) { printf("%02x", salt[i]); } printf("\n"); } else { printf("<empty>\n"); } } bool fromHex(char h, unsigned char *b) { if (h >= '0' && h <= '9') { *b = h - '0'; return true; } else if (h >= 'a' && h <= 'f') { *b = h - 'a' + 10; return true; } else if (h >= 'A' && h <= 'F') { *b = h - 'A' + 10; return true; } return false; } bool hexToByte(char h1, char h2, unsigned char* b) { unsigned char first, second; if (!fromHex(h1, &first)) return false; if (!fromHex(h2, &second)) return false; *b = (first << 4) | second; return true; } /* * Parse args. */ int main(int argc, char* const argv[]) { int opt; int option_index = 0; struct PackageInfo package_info; int result = 1; // pessimistically assume an error. if (argc < 2) { wantUsage = 1; goto bail; } while ((opt = getopt_long(argc, argv, ADD_OPTS, longopts, &option_index)) != -1) { switch (opt) { case 0: if (longopts[option_index].flag) break; fprintf(stderr, "'%s' requires an argument\n", longopts[option_index].name); wantUsage = 1; goto bail; case 'n': package_info.packageName = optarg; break; case 'v': { char* end; package_info.packageVersion = strtol(optarg, &end, 10); if (*optarg == '\0' || *end != '\0') { fprintf(stderr, "ERROR: invalid version; should be integer!\n\n"); wantUsage = 1; goto bail; } break; } case 'o': package_info.overlay = true; break; case 's': if (strlen(optarg) != SALT_LEN * 2) { fprintf(stderr, "ERROR: salt must be 8 bytes in hex (e.g., ABCD65031337D00D)\n\n"); wantUsage = 1; goto bail; } package_info.salted = true; unsigned char b; for (int i = 0, j = 0; i < SALT_LEN; i++, j+=2) { if (!hexToByte(optarg[j], optarg[j+1], &b)) { fprintf(stderr, "ERROR: salt must be in hex (e.g., ABCD65031337D00D)\n"); wantUsage = 1; goto bail; } package_info.salt[i] = b; } break; case '?': wantUsage = 1; goto bail; } } if (wantVersion) { fprintf(stderr, "%s %s\n", gProgName, gProgVersion); } if (wantUsage) { goto bail; } #define CHECK_OP(name) \ if (strncmp(op, name, opsize)) { \ fprintf(stderr, "ERROR: unknown function '%s'!\n\n", op); \ wantUsage = 1; \ goto bail; \ } if (optind < argc) { const char* op = argv[optind++]; const int opsize = strlen(op); if (optind >= argc) { fprintf(stderr, "ERROR: filename required!\n\n"); wantUsage = 1; goto bail; } const char* filename = argv[optind++]; switch (op[0]) { case 'a': CHECK_OP("add"); if (package_info.packageName == NULL) { fprintf(stderr, "ERROR: arguments required 'packageName' and 'version'\n"); goto bail; } doAdd(filename, &package_info); break; case 'r': CHECK_OP("remove"); doRemove(filename); break; case 'i': CHECK_OP("info"); doInfo(filename); break; default: fprintf(stderr, "ERROR: unknown command '%s'!\n\n", op); wantUsage = 1; goto bail; } } bail: if (wantUsage) { usage(); result = 2; } return result; }