ruby_apk 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,435 @@
1
+
2
+ module Android
3
+ class Dex
4
+ # parsing dex object
5
+ # @see http://source.android.com/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/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/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/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/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/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/tech/dalvik/dex-format.html
177
+ class StringDataItem < DexObject
178
+ def to_s
179
+ @params[:data]
180
+ end
181
+ private
182
+ def parse
183
+ @params[:utf16_size] = read_uleb
184
+ @params[:data] = @data[@offset + @parsing_off, @params[:utf16_size]]
185
+ end
186
+ end
187
+
188
+ # type_id_item
189
+ # @see http://source.android.com/tech/dalvik/dex-format.html
190
+ class TypeIdItem < IdsList
191
+ def [](idx)
192
+ raise ArgumentError if idx >= @params[:descriptor_idx].size or idx < 0
193
+ @params[:descriptor_idx][idx]
194
+ end
195
+
196
+ private
197
+ def parse
198
+ @params[:descriptor_idx] = read_value_array(:uint, @ids_size)
199
+ end
200
+ end
201
+
202
+ # proto_id_item
203
+ # @see http://source.android.com/tech/dalvik/dex-format.html
204
+ class ProtoIdItem < DexObject
205
+ # return parse data size
206
+ # @return bytes
207
+ # @note this method for DexObject#read_class_array (private method)
208
+ def self.size
209
+ 4 * 3
210
+ end
211
+ private
212
+ def parse
213
+ @params[:shorty_idx] = read_value(:uint)
214
+ @params[:return_type_idx] = read_value(:uint)
215
+ @params[:parameters_off] = read_value(:uint)
216
+ end
217
+ end
218
+
219
+ # field_id_item
220
+ # @see http://source.android.com/tech/dalvik/dex-format.html
221
+ class FieldIdItem < DexObject
222
+ # return parse data size
223
+ # @return bytes
224
+ # @note this method for DexObject#read_class_array (private method)
225
+ def self.size
226
+ 2 * 2 + 4
227
+ end
228
+ private
229
+ def parse
230
+ @params[:class_idx] = read_value(:ushort)
231
+ @params[:type_idx] = read_value(:ushort)
232
+ @params[:name_idx] = read_value(:uint)
233
+ end
234
+ end
235
+
236
+ # method_id_item
237
+ # @see http://source.android.com/tech/dalvik/dex-format.html
238
+ class MethodIdItem < DexObject
239
+ # return parse data size
240
+ # @return bytes
241
+ # @note this method for DexObject#read_class_array (private method)
242
+ def self.size
243
+ 2 * 2 + 4
244
+ end
245
+ def parse
246
+ @params[:class_idx] = read_value(:ushort)
247
+ @params[:proto_idx] = read_value(:ushort)
248
+ @params[:name_idx] = read_value(:uint)
249
+ end
250
+ end
251
+
252
+ # class_def_item
253
+ # @see http://source.android.com/tech/dalvik/dex-format.html
254
+ # @!attribute [r] class_data_item
255
+ # @return [ClassDataItem] class_data_item of this class
256
+ class ClassDefItem < DexObject
257
+ # @return [Integer] bytes
258
+ def self.size
259
+ 4 * 8
260
+ end
261
+
262
+ def class_data_item
263
+ # description of class_data_off of class_def_item.
264
+ # offset from the start of the file to the associated class data
265
+ # for this item, or 0 if there is no class data for this class.
266
+ if @params[:class_data_off] != 0
267
+ @class_data_item ||= ClassDataItem.new(@data, @params[:class_data_off])
268
+ else
269
+ nil
270
+ end
271
+ end
272
+
273
+ private
274
+ def parse
275
+ @params[:class_idx] = read_value(:uint)
276
+ @params[:access_flags] = read_value(:uint)
277
+ @params[:superclass_idx] = read_value(:uint)
278
+ @params[:interfaces_off] = read_value(:uint)
279
+ @params[:source_file_idx] = read_value(:uint)
280
+ @params[:annotations_off] = read_value(:uint)
281
+ @params[:class_data_off] = read_value(:uint)
282
+ @params[:static_values_off] = read_value(:uint) # TODO: not implemented encoded_array_item
283
+ end
284
+ end
285
+
286
+ # class_data_item
287
+ # @see http://source.android.com/tech/dalvik/dex-format.html
288
+ class ClassDataItem < DexObject
289
+ private
290
+ def parse
291
+ @params[:static_fields_size] = read_uleb
292
+ @params[:instance_fields_size] = read_uleb
293
+ @params[:direct_methods_size] = read_uleb
294
+ @params[:virtual_methods_size] = read_uleb
295
+ @params[:static_fields] = read_class_array(EncodedField, @params[:static_fields_size])
296
+ @params[:instance_fields] = read_class_array(EncodedField, @params[:instance_fields_size])
297
+ @params[:direct_methods] = read_class_array(EncodedMethod, @params[:direct_methods_size])
298
+ @params[:virtual_methods] = read_class_array(EncodedMethod, @params[:virtual_methods_size])
299
+ end
300
+ end
301
+
302
+ # encoded_field
303
+ # @see http://source.android.com/tech/dalvik/dex-format.html
304
+ class EncodedField < DexObject
305
+ private
306
+ def parse
307
+ @params[:field_idx_diff] = read_uleb
308
+ @params[:access_flags] = read_uleb
309
+ end
310
+ end
311
+
312
+ # encoded_method
313
+ # @see http://source.android.com/tech/dalvik/dex-format.html
314
+ # @!attribute [r] code_item
315
+ # @return [CodeItem] code_item of the method
316
+ class EncodedMethod < DexObject
317
+ def code_item
318
+ # description of code_off in code_data_item.
319
+ # offset from the start of the file to the code structure for this method,
320
+ # or 0 if this method is either abstract or native.
321
+ unless @params[:code_off] == 0
322
+ @code_item ||= CodeItem.new(@data, @params[:code_off])
323
+ else
324
+ nil
325
+ end
326
+ end
327
+
328
+ private
329
+ def parse
330
+ @params[:method_idx_diff] = read_uleb
331
+ @params[:access_flags] = read_uleb
332
+ @params[:code_off] = read_uleb
333
+ end
334
+ end
335
+
336
+
337
+ # type_list
338
+ # @see http://source.android.com/tech/dalvik/dex-format.html
339
+ class TypeList < DexObject
340
+ private
341
+ def parse
342
+ @params[:size] = read_value(:uint)
343
+ @params[:list] = read_value_array(:ushort, @params[:size])
344
+ end
345
+ end
346
+
347
+ # code_item
348
+ # @see http://source.android.com/tech/dalvik/dex-format.html
349
+ # @!attribute [r] debug_info_item
350
+ # @return [DebugInfoItem] debug_info_item of this code
351
+ class CodeItem < DexObject
352
+ def debug_info_item
353
+ unless @params[:debug_info_off] == 0
354
+ @debug_info_item ||= DebugInfoItem.new(@data, @params[:debug_info_off])
355
+ else
356
+ nil
357
+ end
358
+ end
359
+
360
+ private
361
+ def parse
362
+ @params[:registers_size] = read_value(:ushort)
363
+ @params[:ins_size] = read_value(:ushort)
364
+ @params[:outs_size] = read_value(:ushort)
365
+ @params[:tries_size] = read_value(:ushort)
366
+ @params[:debug_info_off] = read_value(:uint)
367
+ @params[:insns_size] = read_value(:uint) # size of the instructions list
368
+ @params[:insns] = read_value_array(:ushort, @params[:insns_size])
369
+ read_value(:ushort) if ((@params[:insns_size] % 2) == 1) # for four-byte aligned
370
+ if @params[:tries_size] > 0
371
+ # This element is only present if tries_size is non-zero.
372
+ @params[:tries] = read_class_array(TryItem, @params[:tries_size])
373
+ # This element is only present if tries_size is non-zero.
374
+ @params[:handlers] = EncodedCatchHandlerList.new(@data, @offset + @parsing_off)
375
+ @parsing_off += @params[:handlers].size
376
+ end
377
+ end
378
+ end
379
+
380
+ # try_item
381
+ # @see http://source.android.com/tech/dalvik/dex-format.html
382
+ class TryItem < DexObject
383
+ private
384
+ def parse
385
+ @params[:start_addr] = read_value(:uint)
386
+ @params[:insn_count] = read_value(:ushort)
387
+ @params[:handler_off] = read_value(:ushort)
388
+ end
389
+ end
390
+
391
+ # encoded_catch_handler_list
392
+ # @see http://source.android.com/tech/dalvik/dex-format.html
393
+ class EncodedCatchHandlerList < DexObject
394
+ private
395
+ def parse
396
+ @params[:size] = read_uleb
397
+ @params[:list] = read_class_array(EncodedCatchHandler, @params[:size])
398
+ end
399
+ end
400
+
401
+ # encoded_catch_handler
402
+ # @see http://source.android.com/tech/dalvik/dex-format.html
403
+ class EncodedCatchHandler < DexObject
404
+ private
405
+ def parse
406
+ @params[:size] = read_sleb
407
+ @params[:list] = read_class_array(EncodedTypeAddrPair, @params[:size].abs)
408
+ @params[:catch_all_addr] = read_uleb if @params[:size] < 0
409
+ end
410
+ end
411
+
412
+ # encoded_type_addr_pair
413
+ # @see http://source.android.com/tech/dalvik/dex-format.html
414
+ class EncodedTypeAddrPair < DexObject
415
+ private
416
+ def parse
417
+ @params[:type_idx] = read_uleb
418
+ @params[:addr] = read_uleb
419
+ end
420
+ end
421
+
422
+ # debug_info_item
423
+ # @see http://source.android.com/tech/dalvik/dex-format.html
424
+ class DebugInfoItem < DexObject
425
+ private
426
+ def parse
427
+ @params[:line_start] = read_uleb
428
+ @params[:parameters_size] = read_uleb
429
+ @params[:parameter_names] = []
430
+ @params[:parameters_size].times { @params[:parameter_names] << read_uleb128p1 }
431
+ end
432
+ end
433
+ end
434
+ end
435
+ end
@@ -0,0 +1,151 @@
1
+ require_relative 'dex_object'
2
+
3
+ module Android
4
+ class Dex
5
+ # class information in dex
6
+ # @!attribute [r] name
7
+ # @return [String] class name
8
+ # @!attribute [r] super_class
9
+ # @return [String] super class name
10
+ class ClassInfo
11
+ # no index flag
12
+ NO_INDEX = 0xffffffff
13
+
14
+ # @return [ClassAccessFlag]
15
+ attr_reader :access_flags
16
+ # @return [Array<FieldInfo>] static fields
17
+ attr_reader :static_fields
18
+ # @return [Array<FieldInfo>] instance fields
19
+ attr_reader :instance_fields
20
+ # @return [Array<MethodInfo>] direct methods
21
+ attr_reader :direct_methods
22
+ # @return [Array<MethodInfo>] virtual methods
23
+ attr_reader :virtual_methods
24
+
25
+ # @return [DexObject::ClassDataItem]
26
+ attr_reader :class_data
27
+ # @return [DexObject::ClassDefItem]
28
+ attr_reader :class_def
29
+
30
+ def name
31
+ @dex.type_resolve(@class_def[:class_idx])
32
+ end
33
+ def super_class
34
+ if @class_def[:superclass_idx] != NO_INDEX
35
+ @super_class = @dex.type_resolve(@class_def[:superclass_idx])
36
+ else
37
+ nil
38
+ end
39
+ end
40
+ # @param [Dex::ClassDefItem] class_def
41
+ # @param [Dex] dex dex class instance
42
+ def initialize(class_def, dex)
43
+ @class_def = class_def
44
+ @dex = dex
45
+ @access_flags = ClassAccessFlag.new(@class_def[:access_flags])
46
+ @class_data = @class_def.class_data_item
47
+ @static_fields = @instance_fields = @direct_methods = @virtual_methods = []
48
+ unless @class_data.nil?
49
+ @static_fields = cls2info(@class_data[:static_fields], FieldInfo, :field_idx_diff)
50
+ @instance_fields = cls2info(@class_data[:instance_fields], FieldInfo, :field_idx_diff)
51
+ @direct_methods = cls2info(@class_data[:direct_methods], MethodInfo, :method_idx_diff)
52
+ @virtual_methods = cls2info(@class_data[:virtual_methods], MethodInfo, :method_idx_diff)
53
+ end
54
+ end
55
+
56
+ # @return [String] class difinition
57
+ def definition
58
+ ret = "#{access_flags} class #{name}"
59
+ super_class.nil? ? ret : ret + " extends #{super_class}"
60
+ end
61
+
62
+ private
63
+ def cls2info(arr, cls, idx_key)
64
+ idx = 0
65
+ ret = []
66
+ arr.each do |item|
67
+ idx += item[idx_key]
68
+ ret << cls.new(item, idx, @dex)
69
+ end
70
+ ret
71
+ end
72
+ end
73
+
74
+ # field info object
75
+ # @!attribute [r] name
76
+ # @return [String] field name
77
+ # @!attribute [r] type
78
+ # @return [String] field type
79
+ class FieldInfo
80
+ # @return [ClassAccessFlag]
81
+ attr_reader :access_flags
82
+
83
+ def name
84
+ @dex.strings[@dex.field_ids[@field_id][:name_idx]]
85
+ end
86
+ def type
87
+ @dex.type_resolve(@dex.field_ids[@field_id][:type_idx])
88
+ end
89
+ def initialize(encoded_field, field_id, dex)
90
+ @dex = dex
91
+ @encoded_field = encoded_field
92
+ @field_id = field_id
93
+ @access_flags = ClassAccessFlag.new(encoded_field[:access_flags])
94
+ end
95
+
96
+ # @return [String] field definition
97
+ def definition
98
+ "#{@access_flags.to_s} #{type} #{name}"
99
+ end
100
+ end
101
+
102
+ # method info object
103
+ # @!attribute [r] name
104
+ # @return [String] method name
105
+ # @!attribute [r] ret_type
106
+ # @return [String] return type of the method
107
+ # @!attribute [r] parameters
108
+ # @return [Array<String>] method parameters
109
+ class MethodInfo
110
+ # @return [MethodAccessFlag]
111
+ attr_reader :access_flags
112
+
113
+ def initialize(encoded_method, method_id, dex)
114
+ @encoded_method = encoded_method
115
+ @method_id = method_id
116
+ @dex = dex
117
+ @access_flags = MethodAccessFlag.new(encoded_method[:access_flags])
118
+ end
119
+ def name
120
+ @dex.strings[@dex.method_ids[@method_id][:name_idx]]
121
+ end
122
+ def ret_type
123
+ @dex.type_resolve(proto[:return_type_idx])
124
+ end
125
+ def parameters
126
+ unless proto[:parameters_off] == 0
127
+ list = DexObject::TypeList.new(@dex.data, proto[:parameters_off])
128
+ list[:list].map { |item| @dex.type_resolve(item) }
129
+ else
130
+ []
131
+ end
132
+ end
133
+
134
+ # @return [String] method definition string
135
+ def definition
136
+ "#{access_flags.to_s} #{ret_type} #{name}(#{parameters.join(', ')});"
137
+ end
138
+
139
+ # @return [DexObject::CodeItem]
140
+ def code_item
141
+ @encoded_method.code_item
142
+ end
143
+
144
+ private
145
+ def proto
146
+ @dex.proto_ids[@dex.method_ids[@method_id][:proto_idx]]
147
+ end
148
+ end
149
+
150
+ end
151
+ end
@@ -0,0 +1,45 @@
1
+ module Android
2
+ class Dex
3
+ class << self
4
+ # parse uleb128(unsigned integer) data
5
+ # @param [String] data target byte data
6
+ # @param [Integer] offset
7
+ # @return [Integer, Integer] parsed value and parsed byte length
8
+ # @see http://en.wikipedia.org/wiki/LEB128
9
+ def uleb128(data, offset=0)
10
+ result = 0
11
+ shift = 0
12
+ d = data[offset...data.size]
13
+ (0..4).each do |i|
14
+ byte = d.getbyte(i)
15
+ result |= ((byte & 0x7f) << shift)
16
+ return result, i+1 if ((byte & 0x80) == 0)
17
+ shift += 7
18
+ end
19
+ end
20
+ # parse uleb128 + 1 data
21
+ # @param [String] data target byte data
22
+ # @param [Integer] offset
23
+ # @return [Integer, Integer] parsed value and parsed byte length
24
+ def uleb128p1(data, offset=0)
25
+ ret, len = self.uleb128(data, offset)
26
+ return (ret - 1), len
27
+ end
28
+ # parse sleb128(signed integer) data
29
+ # @param [String] data target byte data
30
+ # @param [Integer] offset
31
+ # @return [Integer, Integer] parsed value and parsed byte length
32
+ def sleb128(data, offset=0)
33
+ result = 0
34
+ shift = 0
35
+ d = data[offset...data.size]
36
+ (0..4).each do |i|
37
+ byte = d.getbyte(i)
38
+ result |=((byte & 0x7F) << shift)
39
+ return (0 == (byte & 0x40) ? result : result - (1 << (shift+7))), i+1 if ((byte & 0x80) == 0)
40
+ shift += 7
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end