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