/
abx_parser.erl
110 lines (97 loc) · 5.18 KB
/
abx_parser.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
-module(abx_parser).
-export([parse_file/1, parse_binary/1, read_xml/1]).
-define(SP(X), case X of 16#FFFFFFFF -> null; _ -> lists:nth((X) + 1, StringPool) end).
-define(MANIFEST, "AndroidManifest.xml").
-include("abx.hrl").
parse_file(FileName) ->
parse_binary(read_xml(FileName)).
read_xml(FileName) ->
case filename:extension(FileName) of
".apk" -> extract_manifest(FileName);
_ -> {ok, C} = file:read_file(FileName), C
end.
extract_manifest(FileName) ->
{ok, [{?MANIFEST, Contents}]} = zip:extract(FileName,
[{file_list, [?MANIFEST]}, memory, cooked]),
Contents.
parse_binary(<<?RES_XML_TYPE:16/little, 8:16/little,
ChunkSize:32/little, Rest/binary>> = Chunk) when byte_size(Chunk) =:= ChunkSize ->
parse_chunks(Rest, []).
parse_chunks(<<>>, Acc) -> lists:reverse(Acc);
parse_chunks(<<ChunkType:16/little, HeaderSize:16/little, ChunkSize:32/little, Rest/binary>>, Acc) ->
PayloadSize = ChunkSize - 8,
<<Payload:PayloadSize/binary, Next/binary>> = Rest,
Acc2 = parse_chunk(ChunkType, HeaderSize, ChunkSize, Payload, Acc),
parse_chunks(Next, Acc2).
parse_chunk(?RES_STRING_POOL_TYPE, HeaderSize, ChunkSize, Payload, Acc) ->
[{string_pool, parse_string_pool(HeaderSize, ChunkSize, Payload)} | Acc];
parse_chunk(?RES_XML_RESOURCE_MAP_TYPE, _HeaderSize, _ChunkSize, Payload, Acc) ->
[{res_map, [R || <<R:32/little>> <= Payload]} | Acc];
parse_chunk(?RES_XML_START_NAMESPACE_TYPE, _HeaderSize, _ChunkSize,
<<LineNum:32/little, Comment:32/little, Prefix:32/little, URI:32/little>>, Acc) ->
StringPool = proplists:get_value(string_pool, Acc),
[{start_ns, LineNum, Comment, lists:nth(Prefix + 1, StringPool),
lists:nth(URI + 1, StringPool)} | Acc];
parse_chunk(?RES_XML_END_NAMESPACE_TYPE, _HeaderSize, _ChunkSize,
<<LineNum:32/little, Comment:32/little, Prefix:32/little, URI:32/little>>, Acc) ->
StringPool = proplists:get_value(string_pool, Acc),
[{end_ns, LineNum, Comment, lists:nth(Prefix + 1, StringPool),
lists:nth(URI + 1, StringPool)} | Acc];
parse_chunk(?RES_XML_START_ELEMENT_TYPE, _HeaderSize, _ChunkSize,
<<LineNum:32/little, Comment:32/little, NsIndex:32/little, NameIndex:32/little,
AttrStart:16/little, AttrSize:16/little, AttrCount:16/little,
0:48, AttrBytes/binary>>, Acc) ->
AttrStart = 20,
AttrSize = 20,
StringPool = proplists:get_value(string_pool, Acc),
Attributes = parse_attributes(AttrBytes, AttrCount, StringPool),
[{element, LineNum, Comment, ?SP(NsIndex), ?SP(NameIndex), Attributes} | Acc];
parse_chunk(?RES_XML_END_ELEMENT_TYPE, _HeaderSize, _ChunkSize,
<<LineNum:32/little, Comment:32/little, NsIndex:32/little, NameIndex:32/little>>, Acc) ->
StringPool = proplists:get_value(string_pool, Acc),
[{end_element, LineNum, Comment, ?SP(NsIndex), ?SP(NameIndex)} | Acc].
parse_attributes(Payload, Count, StringPool) -> parse_attributes(Payload, Count, StringPool, []).
parse_attributes(<<>>, 0, _StringPool, Acc) -> lists:reverse(Acc);
parse_attributes(<<NsIndex:32/little, NameIndex:32/little, RawValue:32/little,
TypedValue:8/binary, Rest/binary>>, Count, StringPool, Acc) ->
Value = case RawValue of
16#FFFFFFFF -> parse_typed_value(TypedValue);
_ -> ?SP(RawValue)
end,
Attribute = {?SP(NsIndex), ?SP(NameIndex), Value},
parse_attributes(Rest, Count - 1, StringPool, [Attribute | Acc]).
parse_typed_value(<<8:16/little, 0, ?TYPE_INT_BOOLEAN, 0:32/little>>) -> false;
parse_typed_value(<<8:16/little, 0, ?TYPE_INT_BOOLEAN, _:32>>) -> true;
parse_typed_value(<<8:16/little, 0, ?TYPE_INT_DECIMAL, Ref:32/little>>) -> Ref;
parse_typed_value(<<8:16/little, 0, ?TYPE_INT_HEX, Ref:32/little>>) -> {hex, Ref};
parse_typed_value(<<8:16/little, 0, ?TYPE_REFERENCE, Ref:32/little>>) -> {ref, Ref}.
parse_string_pool(HeaderSize, ChunkSize, <<StringCount:32/little, StyleCount:32/little, Flag:32/little, StringStart:32/little, StyleStart:32/little, Rest/binary>>) ->
StringIndicesByteCount = StringCount * 4,
StyleByteCount = StyleCount * 4,
Flag = 0,
StringStart = HeaderSize + StringIndicesByteCount + StyleByteCount,
<<StringIndices:StringIndicesByteCount/binary, _Styles:StyleByteCount/binary, Strings/binary>> = Rest,
parse_strings(StringIndices, Strings, {HeaderSize, ChunkSize, StringCount, StyleStart}).
parse_strings(StringIndices, Strings, Params) ->
{Result, <<>>} = parse_strings(StringIndices, Strings, [], Params),
lists:reverse(Result).
parse_strings(<<Index:32/little>>, Strings, Acc, {HeaderSize, ChunkSize, StringCount, 0}) ->
StringLen = ChunkSize - Index - HeaderSize - 4 * StringCount,
parse_string(StringLen, Strings, Acc);
parse_strings(<<Index:32/little>>, Strings, Acc, {_, _, _, StyleStart}) ->
StringLen = StyleStart - Index,
parse_string(StringLen, Strings, Acc);
parse_strings(<<Index:32/little, NextIndex:32/little, Indices/binary>>, Strings, Acc, Params) ->
StringLen = NextIndex - Index,
{Acc2, Strings2} = parse_string(StringLen, Strings, Acc),
parse_strings(<<NextIndex:32/little, Indices/binary>>, Strings2, Acc2, Params).
parse_string(Length, <<Len1, Len2, Payload/binary>>, Acc) ->
ActualLength = case Len1 of
Len2 -> Len2;
_ -> Len1 bor (Len2 bsl 8)
end,
BinaryLength = Length - 2,
<<String:BinaryLength/binary, Rest/binary>> = Payload,
Result = binary:replace(String, <<0>>, <<>>, [global]),
ActualLength = byte_size(Result),
{[Result | Acc], Rest}.