1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import bytecode
20
21 import misc
22 from bytecode import SV, SVs
23 from dvm_permissions import DVM_PERMISSIONS
24
25 import sys, re, types, string, zipfile, StringIO
26 from collections import namedtuple
27 from struct import pack, unpack, calcsize
28 from xml.dom import minidom
29
30 try :
31 import chilkat
32 ZIPMODULE = 0
33
34 CHILKAT_KEY = "testme"
35 except ImportError :
36 ZIPMODULE = 1
37
38
41 self.files = []
42 self.zip = chilkat.CkZip()
43
44 self.zip.UnlockComponent( CHILKAT_KEY )
45
46 self.zip.OpenFromMemory( raw, len(raw) )
47
48 filename = chilkat.CkString()
49 e = self.zip.FirstEntry()
50 while e != None :
51 e.get_FileName(filename)
52 self.files.append( filename.getString() )
53 e = e.NextEntry()
54
57
58 - def read(self, elem) :
59 e = self.zip.GetEntryByName( elem )
60 s = chilkat.CkByteData()
61
62 e.Inflate( s )
63 return s.getBytes()
64
65
67 """APK manages apk file format"""
68 - def __init__(self, filename, raw=False) :
69 """
70 @param filename : specify the path of the file, or raw data
71 @param raw : specify (boolean) if the filename is a path or raw data
72 """
73 self.filename = filename
74
75 self.xml = {}
76 self.package = ""
77 self.androidversion = {}
78 self.permissions = []
79 self.validAPK = False
80
81 if raw == True :
82 self.__raw = filename
83 else :
84 fd = open( filename, "rb" )
85 self.__raw = fd.read()
86 fd.close()
87
88 if ZIPMODULE == 0 :
89 self.zip = ChilkatZip( self.__raw )
90 else :
91 self.zip = zipfile.ZipFile( StringIO.StringIO( self.__raw ) )
92
93
94
95
96 for i in self.zip.namelist() :
97 if i == "AndroidManifest.xml" :
98 self.xml[i] = minidom.parseString( AXMLPrinter( self.zip.read( i ) ).getBuff() )
99
100 self.package = self.xml[i].documentElement.getAttribute( "package" )
101 self.androidversion["Code"] = self.xml[i].documentElement.getAttribute( "android:versionCode" )
102 self.androidversion["Name"] = self.xml[i].documentElement.getAttribute( "android:versionName")
103
104 for item in self.xml[i].getElementsByTagName('uses-permission') :
105 self.permissions.append( str( item.getAttribute("android:name") ) )
106
107 self.validAPK = True
108
111
112
113
114
115
116
117
118
119
120
122 """
123 Return the filename of the APK
124 """
125 return self.filename
126
128 """
129 Return the name of the package
130 """
131 return self.package
132
134 """
135 Return the android version code
136 """
137 return self.androidversion["Code"]
138
140 """
141 Return the android version name
142 """
143 return self.androidversion["Name"]
144
146 """
147 Return the files inside the APK
148 """
149 return self.zip.namelist()
150
152 """
153 Return the files inside the APK with their types (by using python-magic)
154 """
155 try :
156 import magic
157 except ImportError :
158 return {}
159
160 l = {}
161 m = magic.Magic()
162 for i in self.get_files() :
163 l[ i ] = m.from_buffer( self.zip.read( i ) )
164
165 return l
166
168 """
169 Return raw bytes of the APK
170 """
171 return self.__raw
172
174 """
175 Return the raw data of the classes dex file
176 """
177 try :
178 return self.zip.read("classes.dex")
179 except KeyError :
180 return ""
181
183 """
184 Return elements in xml files which match with the tag name and the specific attribute
185
186 @param tag_name : a string which specify the tag name
187 @param attribute : a string which specify the attribute
188 """
189 l = []
190 for i in self.xml :
191 for item in self.xml[i].getElementsByTagName(tag_name) :
192 value = item.getAttribute(attribute)
193
194 if len(value) > 0 and value[0] == "." :
195 value = self.package + value
196
197 l.append( str( value ) )
198 return l
199
201 """
202 Return element in xml files which match with the tag name and the specific attribute
203
204 @param tag_name : a string which specify the tag name
205 @param attribute : a string which specify the attribute
206 """
207 l = []
208 for i in self.xml :
209 for item in self.xml[i].getElementsByTagName(tag_name) :
210 value = item.getAttribute(attribute)
211
212 if len(value) > 0 :
213 return value
214 return None
215
217 """
218 Return the android:name attribute of all activities
219 """
220 return self.get_elements("activity", "android:name")
221
223 """
224 Return the android:name attribute of all services
225 """
226 return self.get_elements("service", "android:name")
227
229 """
230 Return the android:name attribute of all receivers
231 """
232 return self.get_elements("receiver", "android:name")
233
235 """
236 Return the android:name attribute of all providers
237 """
238 return self.get_elements("provider", "android:name")
239
241 """
242 Return permissions
243 """
244 return self.permissions
245
247 """
248 Return permissions with details
249 """
250 l = {}
251
252 for i in self.permissions :
253 perm = i
254 pos = i.rfind(".")
255
256 if pos != -1 :
257 perm = i[pos+1:]
258
259 try :
260 l[ i ] = DVM_PERMISSIONS["MANIFEST_PERMISSION"][ perm ]
261 except KeyError :
262 l[ i ] = "Unknown permission from android reference"
263
264 return l
265
267 """
268 Return the android:minSdkVersion attribute
269 """
270 return self.get_element( "uses-sdk", "android:minSdkVersion" )
271
273 """
274 Return the android:targetSdkVersion attribute
275 """
276 return self.get_element( "uses-sdk", "android:targetSdkVersion" )
277
279 """
280 Return the android:name attributes for libraries
281 """
282 return self.get_elements( "uses-library", "android:name" )
283
292
293
294
297 buff.read( 4 )
298
299 self.chunkSize = SV( '<L', buff.read( 4 ) )
300 self.stringCount = SV( '<L', buff.read( 4 ) )
301 self.styleOffsetCount = SV( '<L', buff.read( 4 ) )
302
303
304 buff.read(4)
305
306 self.stringsOffset = SV( '<L', buff.read( 4 ) )
307 self.stylesOffset = SV( '<L', buff.read( 4 ) )
308
309 self.m_stringOffsets = []
310 self.m_styleOffsets = []
311 self.m_strings = []
312 self.m_styles = []
313
314 for i in range(0, self.stringCount.get_value()) :
315 self.m_stringOffsets.append( SV( '<L', buff.read( 4 ) ) )
316
317 for i in range(0, self.styleOffsetCount.get_value()) :
318 self.m_stylesOffsets.append( SV( '<L', buff.read( 4 ) ) )
319
320 size = self.chunkSize.get_value() - self.stringsOffset.get_value()
321 if self.stylesOffset.get_value() != 0 :
322 size = self.stylesOffset.get_value() - self.stringsOffset.get_value()
323
324
325 if (size%4) != 0 :
326 pass
327
328 for i in range(0, size / 4) :
329 self.m_strings.append( SV( '=L', buff.read( 4 ) ) )
330
331 if self.stylesOffset.get_value() != 0 :
332 size = self.chunkSize.get_value() - self.stringsOffset.get_value()
333
334
335 if (size%4) != 0 :
336 pass
337
338 for i in range(0, size / 4) :
339 self.m_styles.append( SV( '=L', buff.read( 4 ) ) )
340
342 if idx < 0 or self.m_stringOffsets == [] or idx >= len(self.m_stringOffsets) :
343 return None
344
345 offset = self.m_stringOffsets[ idx ].get_value()
346 length = self.getShort(self.m_strings, offset)
347
348 data = ""
349
350 while length > 0 :
351 offset += 2
352
353 data += unichr( self.getShort(self.m_strings, offset) )
354
355
356 if data[-1] == "&" :
357 data = data[:-1]
358
359 length -= 1
360
361 return data
362
364 value = array[offset/4].get_value()
365 if ((offset%4)/2) == 0 :
366 return value & 0xFFFF
367 else :
368 return value >> 16
369
370 ATTRIBUTE_IX_NAMESPACE_URI = 0
371 ATTRIBUTE_IX_NAME = 1
372 ATTRIBUTE_IX_VALUE_STRING = 2
373 ATTRIBUTE_IX_VALUE_TYPE = 3
374 ATTRIBUTE_IX_VALUE_DATA = 4
375 ATTRIBUTE_LENGHT = 5
376
377 CHUNK_AXML_FILE = 0x00080003
378 CHUNK_RESOURCEIDS = 0x00080180
379 CHUNK_XML_FIRST = 0x00100100
380 CHUNK_XML_START_NAMESPACE = 0x00100100
381 CHUNK_XML_END_NAMESPACE = 0x00100101
382 CHUNK_XML_START_TAG = 0x00100102
383 CHUNK_XML_END_TAG = 0x00100103
384 CHUNK_XML_TEXT = 0x00100104
385 CHUNK_XML_LAST = 0x00100104
386
387 START_DOCUMENT = 0
388 END_DOCUMENT = 1
389 START_TAG = 2
390 END_TAG = 3
391 TEXT = 4
394 self.reset()
395
396 self.buff = bytecode.BuffHandle( raw_buff )
397
398 self.buff.read(4)
399 self.buff.read(4)
400
401 self.sb = StringBlock( self.buff )
402
403 self.m_resourceIDs = []
404 self.m_prefixuri = {}
405 self.m_uriprefix = {}
406 self.m_prefixuriL = []
407
409 self.m_event = -1
410 self.m_lineNumber = -1
411 self.m_name = -1
412 self.m_namespaceUri = -1
413 self.m_attributes = []
414 self.m_idAttribute = -1
415 self.m_classAttribute = -1
416 self.m_styleAttribute = -1
417
419 self.doNext()
420 return self.m_event
421
423 if self.m_event == END_DOCUMENT :
424 return
425
426 event = self.m_event
427
428 self.reset()
429 while 1 :
430 chunkType = -1
431
432
433 if event == END_TAG :
434 pass
435
436
437 if event == START_DOCUMENT :
438 chunkType = CHUNK_XML_START_TAG
439 else :
440 if self.buff.end() == True :
441 self.m_event = END_DOCUMENT
442 break
443 chunkType = SV( '<L', self.buff.read( 4 ) ).get_value()
444
445
446 if chunkType == CHUNK_RESOURCEIDS :
447 chunkSize = SV( '<L', self.buff.read( 4 ) ).get_value()
448
449 if chunkSize < 8 or chunkSize%4 != 0 :
450 raise("ooo")
451
452 for i in range(0, chunkSize/4-2) :
453 self.m_resourceIDs.append( SV( '<L', self.buff.read( 4 ) ) )
454
455 continue
456
457
458 if chunkType < CHUNK_XML_FIRST or chunkType > CHUNK_XML_LAST :
459 raise("ooo")
460
461
462 if chunkType == CHUNK_XML_START_TAG and event == -1 :
463 self.m_event = START_DOCUMENT
464 break
465
466 self.buff.read( 4 )
467 lineNumber = SV( '<L', self.buff.read( 4 ) ).get_value()
468 self.buff.read( 4 )
469
470 if chunkType == CHUNK_XML_START_NAMESPACE or chunkType == CHUNK_XML_END_NAMESPACE :
471 if chunkType == CHUNK_XML_START_NAMESPACE :
472 prefix = SV( '<L', self.buff.read( 4 ) ).get_value()
473 uri = SV( '<L', self.buff.read( 4 ) ).get_value()
474
475 self.m_prefixuri[ prefix ] = uri
476 self.m_uriprefix[ uri ] = prefix
477 self.m_prefixuriL.append( (prefix, uri) )
478 else :
479 self.buff.read( 4 )
480 self.buff.read( 4 )
481 (prefix, uri) = self.m_prefixuriL.pop()
482
483
484
485 continue
486
487
488 self.m_lineNumber = lineNumber
489
490 if chunkType == CHUNK_XML_START_TAG :
491 self.m_namespaceUri = SV( '<L', self.buff.read( 4 ) ).get_value()
492 self.m_name = SV( '<L', self.buff.read( 4 ) ).get_value()
493
494
495 self.buff.read( 4 )
496
497 attributeCount = SV( '<L', self.buff.read( 4 ) ).get_value()
498 self.m_idAttribute = (attributeCount>>16) - 1
499 attributeCount = attributeCount & 0xFFFF
500 self.m_classAttribute = SV( '<L', self.buff.read( 4 ) ).get_value()
501 self.m_styleAttribute = (self.m_classAttribute>>16) - 1
502
503 self.m_classAttribute = (self.m_classAttribute & 0xFFFF) - 1
504
505 for i in range(0, attributeCount*ATTRIBUTE_LENGHT) :
506 self.m_attributes.append( SV( '<L', self.buff.read( 4 ) ).get_value() )
507
508 for i in range(ATTRIBUTE_IX_VALUE_TYPE, len(self.m_attributes), ATTRIBUTE_LENGHT) :
509 self.m_attributes[i] = (self.m_attributes[i]>>24)
510
511 self.m_event = START_TAG
512 break
513
514 if chunkType == CHUNK_XML_END_TAG :
515 self.m_namespaceUri = SV( '<L', self.buff.read( 4 ) ).get_value()
516 self.m_name = SV( '<L', self.buff.read( 4 ) ).get_value()
517 self.m_event = END_TAG
518 break
519
520 if chunkType == CHUNK_XML_TEXT :
521 self.m_name = SV( '<L', self.buff.read( 4 ) ).get_value()
522
523
524 self.buff.read( 4 )
525 self.buff.read( 4 )
526
527 self.m_event = TEXT
528 break
529
531 try :
532 return self.m_uriprefix[ uri ]
533 except KeyError :
534 return -1
535
537 try :
538 return self.sb.getRaw(self.m_prefixuri[ self.m_namespaceUri ])
539 except KeyError :
540 return ""
541
543 if self.m_name == -1 or (self.m_event != START_TAG and self.m_event != END_TAG) :
544 return ""
545
546 return self.sb.getRaw(self.m_name)
547
548 - def getText(self) :
549 if self.m_name == -1 or self.m_event != TEXT :
550 return ""
551
552 return self.sb.getRaw(self.m_name)
553
555 prefix = self.m_prefixuriL[ pos ][0]
556 return self.sb.getRaw( prefix )
557
559 uri = self.m_prefixuriL[ pos ][1]
560 return self.sb.getRaw( uri )
561
564
566
567 if self.m_event != START_TAG :
568 raise("Current event is not START_TAG.")
569
570 offset = index * 5
571
572 if offset >= len(self.m_attributes) :
573 raise("Invalid attribute index")
574
575 return offset
576
582
592
601
605
609
618
619
620
621 TYPE_ATTRIBUTE = 2
622 TYPE_DIMENSION = 5
623 TYPE_FIRST_COLOR_INT = 28
624 TYPE_FIRST_INT = 16
625 TYPE_FLOAT = 4
626 TYPE_FRACTION = 6
627 TYPE_INT_BOOLEAN = 18
628 TYPE_INT_COLOR_ARGB4 = 30
629 TYPE_INT_COLOR_ARGB8 = 28
630 TYPE_INT_COLOR_RGB4 = 31
631 TYPE_INT_COLOR_RGB8 = 29
632 TYPE_INT_DEC = 16
633 TYPE_INT_HEX = 17
634 TYPE_LAST_COLOR_INT = 31
635 TYPE_LAST_INT = 31
636 TYPE_NULL = 0
637 TYPE_REFERENCE = 1
638 TYPE_STRING = 3
639
640 RADIX_MULTS = [ 0.00390625, 3.051758E-005, 1.192093E-007, 4.656613E-010 ]
641 DIMENSION_UNITS = [ "px","dip","sp","pt","in","mm","","" ]
642 FRACTION_UNITS = [ "%","%p","","","","","","" ]
643
644 COMPLEX_UNIT_MASK = 15
645
680
682 return self.buff.encode("utf-8")
683
685 if prefix == None or len(prefix) == 0 :
686 return ""
687
688 return prefix + ":"
689
729
731 return (float)(xcomplex & 0xFFFFFF00)*RADIX_MULTS[(xcomplex>>4) & 3];
732
734 if id >> 24 == 1 :
735 return "android:"
736 return ""
737