You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
302 lines
8.1 KiB
302 lines
8.1 KiB
/*
|
|
* 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, 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;
|
|
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;
|
|
}
|