ruby_apk 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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