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