ruby_android 0.0.2 → 0.7.2
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.
- checksums.yaml +4 -4
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +5 -0
- data/.idea/modules.xml +9 -0
- data/.idea/ruby_apk.iml +51 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/vcs.xml +7 -0
- data/.idea/workspace.xml +508 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +51 -0
- data/Gemfile +3 -3
- data/Gemfile.lock +73 -0
- data/LICENSE.txt +2 -2
- data/Rakefile +42 -1
- data/VERSION +1 -0
- data/lib/android/apk.rb +207 -0
- data/lib/android/axml_parser.rb +173 -0
- data/lib/android/dex/access_flag.rb +74 -0
- data/lib/android/dex/dex_object.rb +475 -0
- data/lib/android/dex/info.rb +151 -0
- data/lib/android/dex/utils.rb +45 -0
- data/lib/android/dex.rb +92 -0
- data/lib/android/layout.rb +44 -0
- data/lib/android/manifest.rb +249 -0
- data/lib/android/resource.rb +529 -0
- data/lib/android/utils.rb +55 -0
- data/lib/ruby_apk.rb +7 -0
- data/ruby_android.gemspec +103 -17
- data/spec/apk_spec.rb +301 -0
- data/spec/axml_parser_spec.rb +67 -0
- data/spec/data/sample.apk +0 -0
- data/spec/data/sample_AndroidManifest.xml +0 -0
- data/spec/data/sample_classes.dex +0 -0
- data/spec/data/sample_resources.arsc +0 -0
- data/spec/data/sample_resources_utf16.arsc +0 -0
- data/spec/data/str_resources.arsc +0 -0
- data/spec/dex/access_flag_spec.rb +42 -0
- data/spec/dex/dex_object_spec.rb +118 -0
- data/spec/dex/info_spec.rb +121 -0
- data/spec/dex/utils_spec.rb +56 -0
- data/spec/dex_spec.rb +59 -0
- data/spec/layout_spec.rb +27 -0
- data/spec/manifest_spec.rb +221 -0
- data/spec/resource_spec.rb +170 -0
- data/spec/ruby_apk_spec.rb +4 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/utils_spec.rb +90 -0
- metadata +112 -27
- data/.gitignore +0 -14
- data/lib/ruby_android/version.rb +0 -3
- data/lib/ruby_android.rb +0 -7
@@ -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
|
@@ -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
|