android_parser 2.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,49 @@
1
+ require 'rexml/document'
2
+ require 'stringio'
3
+
4
+ module Android
5
+ class AXMLWriter < AXMLParser
6
+ # @param [String] axml binary xml data
7
+ def initialize(axml)
8
+ @io = StringIO.new(axml, "r+b")
9
+ @strings = []
10
+ end
11
+
12
+ def modify_metadata!(name, new_value)
13
+ parse if @doc.nil?
14
+
15
+ entry = @metadata.find { |meta| meta['android:name'][:value] == name }
16
+ raise "Metadata #{name} could not be found and modified" if entry.nil?
17
+
18
+ pos = if entry['android:value'][:is_string]
19
+ new_string_id = add_string!(new_value)
20
+ new_value = new_string_id
21
+ entry['android:value'][:val_str_id]
22
+ else
23
+ entry['android:value'][:position]
24
+ end
25
+
26
+ @io.pos = pos
27
+ @io.write([new_value].pack('V'))
28
+ end
29
+
30
+ def add_string!(str)
31
+ new_string_id, bytes_added = Resource::ResStringPool.new(@io.string, 8).add_string(str)
32
+
33
+ # Update XML size and positions of metadata attributes.
34
+ @io.pos = 4
35
+ xml_size = @io.read(4).unpack1('V')
36
+ @io.pos = 4
37
+ @io.write([xml_size + bytes_added].pack('V'))
38
+
39
+ @metadata = @metadata.map do |metadata|
40
+ metadata.transform_values do |attribute|
41
+ attribute[:position] += bytes_added
42
+ attribute[:val_str_id] += bytes_added
43
+ attribute
44
+ end
45
+ end
46
+ new_string_id
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,74 @@
1
+
2
+ module Android
3
+ class Dex
4
+ # access flag object
5
+ class AccessFlag
6
+ # @return [Integer] flag value
7
+ attr_reader :flag
8
+ def initialize(flag)
9
+ @flag = flag
10
+ end
11
+ end
12
+
13
+ # access flag object for class in dex
14
+ class ClassAccessFlag < AccessFlag
15
+ ACCESSORS = [
16
+ {value:0x1, name:'public'},
17
+ {value:0x2, name:'private'},
18
+ {value:0x4, name:'protected'},
19
+ {value:0x8, name:'static'},
20
+ {value:0x10, name:'final'},
21
+ {value:0x20, name:'synchronized'},
22
+ {value:0x40, name:'volatile'},
23
+ {value:0x80, name:'transient'},
24
+ {value:0x100, name:'native'},
25
+ {value:0x200, name:'interface'},
26
+ {value:0x400, name:'abstract'},
27
+ {value:0x800, name:'strict'},
28
+ {value:0x1000, name:'synthetic'},
29
+ {value:0x2000, name:'annotation'},
30
+ {value:0x4000, name:'enum'},
31
+ #{value:0x8000, name:'unused'},
32
+ {value:0x10000, name:'constructor'},
33
+ {value:0x20000, name:'declared-synchronized'},
34
+ ]
35
+
36
+ # convert access flag to string
37
+ # @return [String]
38
+ def to_s
39
+ ACCESSORS.select{|e| ((e[:value] & @flag) != 0) }.map{|e| e[:name] }.join(' ')
40
+ end
41
+ end
42
+
43
+ # access flag object for method in dex
44
+ class MethodAccessFlag < AccessFlag
45
+ ACCESSORS = [
46
+ {value: 0x1, name:'public'},
47
+ {value: 0x2, name:'private'},
48
+ {value: 0x4, name:'protected'},
49
+ {value: 0x8, name:'static'},
50
+ {value: 0x10, name:'final'},
51
+ {value: 0x20, name:'synchronized'},
52
+ {value: 0x40, name:'bridge'},
53
+ {value: 0x80, name:'varargs'},
54
+ {value: 0x100, name:'native'},
55
+ {value: 0x200, name:'interface'},
56
+ {value: 0x400, name:'abstract'},
57
+ {value: 0x800, name:'strict'},
58
+ {value: 0x1000, name:'synthetic'},
59
+ {value: 0x2000, name:'annotation'},
60
+ {value: 0x4000, name:'enum'},
61
+ #{value: 0x8000, name:'unused'},
62
+ {value: 0x10000, name:'constructor'},
63
+ {value: 0x20000, name:'declared-synchronized'},
64
+ ]
65
+ # convert access flag to string
66
+ # @return [String]
67
+ def to_s
68
+ ACCESSORS.select{|e| ((e[:value] & @flag) != 0) }.map{|e| e[:name] }.join(' ')
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+
@@ -0,0 +1,475 @@
1
+
2
+ module Android
3
+ class Dex
4
+ # parsing dex object
5
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
6
+ class DexObject
7
+ # @return [Integer] object size
8
+ attr_reader :size
9
+
10
+ def initialize(data, offset)
11
+ @data = data
12
+ @offset = offset
13
+
14
+ @params = {}
15
+ @parsing_off = 0 # parsing offset
16
+ parse()
17
+ @size = @parsing_off
18
+ end
19
+
20
+ # returns symbol keys
21
+ # @return [Array<Symbol>] header key
22
+ def symbols
23
+ @params.keys
24
+ end
25
+
26
+ # @param [Symbol] sym should be included in #symbols
27
+ # @return [Object] dex header value which is related with sym
28
+ def [](sym)
29
+ @params[sym.to_sym]
30
+ end
31
+
32
+ # @return [String]
33
+ def inspect
34
+ str = "<#{self.class}\n"
35
+ @params.each do |key,val|
36
+ str.concat " #{key}: #{val}\n"
37
+ end
38
+ str.concat '>'
39
+ end
40
+
41
+ private
42
+ def parse
43
+ raise 'this method should be overloaded.'
44
+ end
45
+
46
+ def read_value(type)
47
+ types = {
48
+ :byte => [1, 'c'],
49
+ :ubyte => [1, 'C'],
50
+ :short => [2, 's'], #ugh!:depend on machine endian
51
+ :ushort => [2, 'v'],
52
+ :int => [4, 'i'], #ugh!:depend on machine endian
53
+ :uint => [4, 'V'],
54
+ :long => [8, 'q'],
55
+ :ulong => [8, 'Q'],
56
+ }
57
+ len, pack_str = types.fetch(type)
58
+ value = @data[@offset+@parsing_off, len].unpack(pack_str)[0]
59
+ @parsing_off += len
60
+ return value
61
+ end
62
+
63
+ # read short int from data buffer
64
+ # @return [Integer] short value
65
+ def read_sleb
66
+ value, len = Dex::sleb128(@data, @offset + @parsing_off)
67
+ @parsing_off += len
68
+ value
69
+ end
70
+ # read integer from data buffer
71
+ # @return [Integer] integer value
72
+ def read_uleb
73
+ value, len = Dex::uleb128(@data, @offset + @parsing_off)
74
+ @parsing_off += len
75
+ value
76
+ end
77
+ # read integer from data buffer and plus 1
78
+ # @return [Integer] integer value
79
+ def read_uleb128p1
80
+ value, len = Dex::uleb128p1(@data, @offset + @parsing_off)
81
+ @parsing_off += len
82
+ value
83
+ end
84
+ # read various values from data buffer as array
85
+ # @param [Symbol] type
86
+ # @param [Integer] size num of data
87
+ # @return [Array] value array
88
+ def read_value_array(type, size)
89
+ ret_array = []
90
+ size.times { ret_array << read_value(type) }
91
+ ret_array
92
+ end
93
+ # read class values from data buffer as array
94
+ # @param [Class] cls target class
95
+ # @param [Integer] size num of data
96
+ # @return [Array<cls>] object array
97
+ def read_class_array(cls, size)
98
+ ret_array = []
99
+ size.times do
100
+ item = cls.new(@data, @offset + @parsing_off)
101
+ ret_array << item
102
+ @parsing_off += item.size
103
+ end
104
+ ret_array
105
+ end
106
+
107
+ public
108
+ # header_item
109
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
110
+ class Header < DexObject
111
+ def initialize(data)
112
+ super(data, 0)
113
+ end
114
+
115
+ private
116
+ def parse
117
+ @params[:magic] = @data[0, 8]
118
+ @parsing_off += 8
119
+ @params[:checksum] = read_value(:uint)
120
+ @params[:signature] = @data[12, 20]
121
+ @parsing_off += 20
122
+ [
123
+ :file_size, :header_size, :endian_tag, :link_size, :link_off, :map_off,
124
+ :string_ids_size, :string_ids_off, :type_ids_size, :type_ids_off,
125
+ :proto_ids_size, :proto_ids_off, :field_ids_size, :field_ids_off,
126
+ :method_ids_size, :method_ids_off, :class_defs_size, :class_defs_off,
127
+ :data_size, :data_off
128
+ ].each do |key|
129
+ @params[key] = read_value(:uint)
130
+ end
131
+ end
132
+ end
133
+
134
+ # map_list
135
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
136
+ class MapList < DexObject
137
+ private
138
+ def parse
139
+ @params[:size] = read_value(:uint)
140
+ @params[:list] = read_class_array(MapItem, @params[:size])
141
+ end
142
+ end
143
+
144
+ # map_item
145
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
146
+ class MapItem < DexObject
147
+ private
148
+ def parse
149
+ @params[:type] = read_value(:short)
150
+ @params[:unused] = read_value(:short)
151
+ @params[:size] = read_value(:uint)
152
+ @params[:offset] = read_value(:uint)
153
+ end
154
+ end
155
+
156
+ # id_list
157
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
158
+ class IdsList < DexObject
159
+ attr_reader :ids_size
160
+ def initialize(data, off, ids_size)
161
+ @ids_size = ids_size
162
+ super(data, off)
163
+ end
164
+ end
165
+
166
+ # string_id_item
167
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
168
+ class StringIdItem < IdsList
169
+ private
170
+ def parse
171
+ @params[:string_data_off] = read_value_array(:uint, @ids_size)
172
+ end
173
+ end
174
+
175
+ # string_data_item
176
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
177
+ class StringDataItem < DexObject
178
+ def to_s
179
+ @params[:data]
180
+ end
181
+ private
182
+ def mutf8_to_utf8(data, off, ulen)
183
+ mi = 0 # index of mutf8 data
184
+ codepoints = []
185
+ while ulen > 0 do
186
+ b0 = data[off + mi].ord
187
+ bu = (b0 & 0xf0) # b0's upper nibble
188
+ if (b0 & 0x80) == 0 # single byte encoding (0b0xxx_xxxx)
189
+ c = b0
190
+ mi += 1
191
+ ulen -= 1
192
+ elsif bu == 0xc0 || bu == 0xd0 # two-byte encoding (0b110x_xxxx)
193
+ b1 = data[off + mi + 1].ord
194
+ c = (b0 & 0x1f) << 6 | (b1 & 0x3f)
195
+ mi += 2
196
+ ulen -= 1
197
+ elsif bu == 0xe0 # three-byte encoding (0b1110_xxxx)
198
+ b1 = data[off + mi + 1].ord
199
+ b2 = data[off + mi + 2].ord
200
+ c = (b0 & 0x0f) << 12 | (b1 & 0x3f) << 6 | (b2 & 0x3f)
201
+ mi += 3
202
+ ulen -= 1
203
+ if 0xD800 <= c && c <= 0xDBFF # this must be a surrogate pair
204
+ b4 = data[off + mi + 1].ord
205
+ b5 = data[off + mi + 2].ord
206
+ c = ((b1 & 0x0f) + 1) << 16 | (b2 & 0x3f) << 10 | (b4 & 0x0f) << 6 | (b5 & 0x3f)
207
+ mi += 3
208
+ ulen -= 1
209
+ end
210
+ else
211
+ STDERR.puts "unsupported byte: 0x#{'%02X' % b0} @#{mi}"
212
+ c = 0
213
+ mi += 1
214
+ next
215
+ end
216
+ if c != 0
217
+ codepoints << c
218
+ end
219
+ end
220
+ codepoints.pack("U*")
221
+ end
222
+ def parse
223
+ @params[:utf16_size] = read_uleb
224
+ @params[:data] = mutf8_to_utf8(@data, @offset + @parsing_off, @params[:utf16_size])
225
+ end
226
+ end
227
+
228
+ # type_id_item
229
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
230
+ class TypeIdItem < IdsList
231
+ def [](idx)
232
+ raise ArgumentError if idx >= @params[:descriptor_idx].size or idx < 0
233
+ @params[:descriptor_idx][idx]
234
+ end
235
+
236
+ private
237
+ def parse
238
+ @params[:descriptor_idx] = read_value_array(:uint, @ids_size)
239
+ end
240
+ end
241
+
242
+ # proto_id_item
243
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
244
+ class ProtoIdItem < DexObject
245
+ # return parse data size
246
+ # @return bytes
247
+ # @note this method for DexObject#read_class_array (private method)
248
+ def self.size
249
+ 4 * 3
250
+ end
251
+ private
252
+ def parse
253
+ @params[:shorty_idx] = read_value(:uint)
254
+ @params[:return_type_idx] = read_value(:uint)
255
+ @params[:parameters_off] = read_value(:uint)
256
+ end
257
+ end
258
+
259
+ # field_id_item
260
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
261
+ class FieldIdItem < DexObject
262
+ # return parse data size
263
+ # @return bytes
264
+ # @note this method for DexObject#read_class_array (private method)
265
+ def self.size
266
+ 2 * 2 + 4
267
+ end
268
+ private
269
+ def parse
270
+ @params[:class_idx] = read_value(:ushort)
271
+ @params[:type_idx] = read_value(:ushort)
272
+ @params[:name_idx] = read_value(:uint)
273
+ end
274
+ end
275
+
276
+ # method_id_item
277
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
278
+ class MethodIdItem < DexObject
279
+ # return parse data size
280
+ # @return bytes
281
+ # @note this method for DexObject#read_class_array (private method)
282
+ def self.size
283
+ 2 * 2 + 4
284
+ end
285
+ def parse
286
+ @params[:class_idx] = read_value(:ushort)
287
+ @params[:proto_idx] = read_value(:ushort)
288
+ @params[:name_idx] = read_value(:uint)
289
+ end
290
+ end
291
+
292
+ # class_def_item
293
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
294
+ # @!attribute [r] class_data_item
295
+ # @return [ClassDataItem] class_data_item of this class
296
+ class ClassDefItem < DexObject
297
+ # @return [Integer] bytes
298
+ def self.size
299
+ 4 * 8
300
+ end
301
+
302
+ def class_data_item
303
+ # description of class_data_off of class_def_item.
304
+ # offset from the start of the file to the associated class data
305
+ # for this item, or 0 if there is no class data for this class.
306
+ if @params[:class_data_off] != 0
307
+ @class_data_item ||= ClassDataItem.new(@data, @params[:class_data_off])
308
+ else
309
+ nil
310
+ end
311
+ end
312
+
313
+ private
314
+ def parse
315
+ @params[:class_idx] = read_value(:uint)
316
+ @params[:access_flags] = read_value(:uint)
317
+ @params[:superclass_idx] = read_value(:uint)
318
+ @params[:interfaces_off] = read_value(:uint)
319
+ @params[:source_file_idx] = read_value(:uint)
320
+ @params[:annotations_off] = read_value(:uint)
321
+ @params[:class_data_off] = read_value(:uint)
322
+ @params[:static_values_off] = read_value(:uint) # TODO: not implemented encoded_array_item
323
+ end
324
+ end
325
+
326
+ # class_data_item
327
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
328
+ class ClassDataItem < DexObject
329
+ private
330
+ def parse
331
+ @params[:static_fields_size] = read_uleb
332
+ @params[:instance_fields_size] = read_uleb
333
+ @params[:direct_methods_size] = read_uleb
334
+ @params[:virtual_methods_size] = read_uleb
335
+ @params[:static_fields] = read_class_array(EncodedField, @params[:static_fields_size])
336
+ @params[:instance_fields] = read_class_array(EncodedField, @params[:instance_fields_size])
337
+ @params[:direct_methods] = read_class_array(EncodedMethod, @params[:direct_methods_size])
338
+ @params[:virtual_methods] = read_class_array(EncodedMethod, @params[:virtual_methods_size])
339
+ end
340
+ end
341
+
342
+ # encoded_field
343
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
344
+ class EncodedField < DexObject
345
+ private
346
+ def parse
347
+ @params[:field_idx_diff] = read_uleb
348
+ @params[:access_flags] = read_uleb
349
+ end
350
+ end
351
+
352
+ # encoded_method
353
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
354
+ # @!attribute [r] code_item
355
+ # @return [CodeItem] code_item of the method
356
+ class EncodedMethod < DexObject
357
+ def code_item
358
+ # description of code_off in code_data_item.
359
+ # offset from the start of the file to the code structure for this method,
360
+ # or 0 if this method is either abstract or native.
361
+ unless @params[:code_off] == 0
362
+ @code_item ||= CodeItem.new(@data, @params[:code_off])
363
+ else
364
+ nil
365
+ end
366
+ end
367
+
368
+ private
369
+ def parse
370
+ @params[:method_idx_diff] = read_uleb
371
+ @params[:access_flags] = read_uleb
372
+ @params[:code_off] = read_uleb
373
+ end
374
+ end
375
+
376
+
377
+ # type_list
378
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
379
+ class TypeList < DexObject
380
+ private
381
+ def parse
382
+ @params[:size] = read_value(:uint)
383
+ @params[:list] = read_value_array(:ushort, @params[:size])
384
+ end
385
+ end
386
+
387
+ # code_item
388
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
389
+ # @!attribute [r] debug_info_item
390
+ # @return [DebugInfoItem] debug_info_item of this code
391
+ class CodeItem < DexObject
392
+ def debug_info_item
393
+ unless @params[:debug_info_off] == 0
394
+ @debug_info_item ||= DebugInfoItem.new(@data, @params[:debug_info_off])
395
+ else
396
+ nil
397
+ end
398
+ end
399
+
400
+ private
401
+ def parse
402
+ @params[:registers_size] = read_value(:ushort)
403
+ @params[:ins_size] = read_value(:ushort)
404
+ @params[:outs_size] = read_value(:ushort)
405
+ @params[:tries_size] = read_value(:ushort)
406
+ @params[:debug_info_off] = read_value(:uint)
407
+ @params[:insns_size] = read_value(:uint) # size of the instructions list
408
+ @params[:insns] = read_value_array(:ushort, @params[:insns_size])
409
+ read_value(:ushort) if ((@params[:insns_size] % 2) == 1) # for four-byte aligned
410
+ if @params[:tries_size] > 0
411
+ # This element is only present if tries_size is non-zero.
412
+ @params[:tries] = read_class_array(TryItem, @params[:tries_size])
413
+ # This element is only present if tries_size is non-zero.
414
+ @params[:handlers] = EncodedCatchHandlerList.new(@data, @offset + @parsing_off)
415
+ @parsing_off += @params[:handlers].size
416
+ end
417
+ end
418
+ end
419
+
420
+ # try_item
421
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
422
+ class TryItem < DexObject
423
+ private
424
+ def parse
425
+ @params[:start_addr] = read_value(:uint)
426
+ @params[:insn_count] = read_value(:ushort)
427
+ @params[:handler_off] = read_value(:ushort)
428
+ end
429
+ end
430
+
431
+ # encoded_catch_handler_list
432
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
433
+ class EncodedCatchHandlerList < DexObject
434
+ private
435
+ def parse
436
+ @params[:size] = read_uleb
437
+ @params[:list] = read_class_array(EncodedCatchHandler, @params[:size])
438
+ end
439
+ end
440
+
441
+ # encoded_catch_handler
442
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
443
+ class EncodedCatchHandler < DexObject
444
+ private
445
+ def parse
446
+ @params[:size] = read_sleb
447
+ @params[:list] = read_class_array(EncodedTypeAddrPair, @params[:size].abs)
448
+ @params[:catch_all_addr] = read_uleb if @params[:size] <= 0
449
+ end
450
+ end
451
+
452
+ # encoded_type_addr_pair
453
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
454
+ class EncodedTypeAddrPair < DexObject
455
+ private
456
+ def parse
457
+ @params[:type_idx] = read_uleb
458
+ @params[:addr] = read_uleb
459
+ end
460
+ end
461
+
462
+ # debug_info_item
463
+ # @see http://source.android.com/devices/tech/dalvik/dex-format.html
464
+ class DebugInfoItem < DexObject
465
+ private
466
+ def parse
467
+ @params[:line_start] = read_uleb
468
+ @params[:parameters_size] = read_uleb
469
+ @params[:parameter_names] = []
470
+ @params[:parameters_size].times { @params[:parameter_names] << read_uleb128p1 }
471
+ end
472
+ end
473
+ end
474
+ end
475
+ end