• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
PackageInfo()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  */
usage(void)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 
doAdd(const char * filename,struct PackageInfo * info)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 
doRemove(const char * filename)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 
doInfo(const char * filename)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 
fromHex(char h,unsigned char * b)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 
hexToByte(char h1,char h2,unsigned char * b)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  */
main(int argc,char * const argv[])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