This repository has been archived by the owner on Nov 9, 2023. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
common.go
182 lines (153 loc) · 4.64 KB
/
common.go
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package apk
import (
"encoding/binary"
"io"
"unicode/utf16"
)
const maxReadBytes = 1 << 26 // 64 MiB
type resChunkHeader struct {
Type uint16
HeaderSize uint16
Size uint32
}
const (
resStringPoolType = 0x1
resTableChunkType = 0x2
resXMLType = 0x3
resXMLStartNamespaceType = 0x100
resXMLEndNamespaceType = 0x101
resXMLStartElementType = 0x102
resXMLEndElementType = 0x103
resXMLCDATAType = 0x0104
resXMLResourceMapType = 0x180
resTablePackageType = 0x200
resTableTypeType = 0x201
resTableTypeSpecType = 0x202
)
type dataType = uint8
const (
typeNull dataType = 0x00
typeReference dataType = 0x01
typeString dataType = 0x03
typeFloat dataType = 0x04
typeIntDec dataType = 0x10
typeIntHex dataType = 0x11
typeIntBoolean dataType = 0x12
)
type resValue struct {
Size uint16
Res0 uint8
DataType dataType
Data uint32
}
type resStringPoolRef struct {
Index uint32
}
type flags uint32
const utf8Flag flags = 1 << 8
type resStringPoolHeader struct {
Header resChunkHeader
StringCount uint32
StyleCount uint32
Flags flags
StringsStart uint32
StylesStart uint32
}
func parseStringPool(sr *io.SectionReader) (map[resStringPoolRef]string, error) {
stringPool := make(map[resStringPoolRef]string)
sp := new(resStringPoolHeader)
if err := binary.Read(sr, binary.LittleEndian, sp); err != nil {
return nil, err
}
sIndices := make([]uint32, sp.StringCount)
if err := binary.Read(sr, binary.LittleEndian, sIndices); err != nil {
return nil, err
}
if sp.Flags&utf8Flag == utf8Flag { // UTF-8
for i, sStart := range sIndices {
if _, err := sr.Seek(int64(sp.StringsStart+sStart), io.SeekStart); err != nil {
return nil, err
}
// First var8 is length in characters and second is length in bytes. We want
// the length in bytes so we skip the first var8.
if _, err := parseVar8Len(sr); err != nil {
return nil, err
}
size, err := parseVar8Len(sr)
if err != nil {
return nil, err
}
buf := make([]uint8, size)
if err := binary.Read(sr, binary.LittleEndian, buf); err != nil {
return nil, err
}
spRef := resStringPoolRef{Index: uint32(i)}
stringPool[spRef] = string(buf)
}
} else { // UTF-16
for i, sStart := range sIndices {
if _, err := sr.Seek(int64(sp.StringsStart+sStart), io.SeekStart); err != nil {
return nil, err
}
size, err := parseVar16Len(sr)
if err != nil {
return nil, err
}
buf := make([]uint16, size)
if err := binary.Read(sr, binary.LittleEndian, buf); err != nil {
return nil, err
}
spRef := resStringPoolRef{Index: uint32(i)}
stringPool[spRef] = string(utf16.Decode(buf))
}
}
return stringPool, nil
}
// parseVar8Len reads a variable-length length for UTF-8 strings.
//
// Strings in UTF-8 format have length indicated by a length encoded in stored data. It is either 1
// or 2 characters of length data. This allows a maximum length of 0x7FFF (32767 bytes), but you
// should consider storing text in another way if you're using that much data in a single string.
//
// If the high bit is set, then there are 2 characters or 2 bytes of length data encoded. In that
// case, we drop the high bit of the first character and add it together with the next character.
func parseVar8Len(sr *io.SectionReader) (int, error) {
var size int
var first, second uint8
if err := binary.Read(sr, binary.LittleEndian, &first); err != nil {
return 0, err
}
if first&0x80 != 0 { // high bit is set, read next byte
if err := binary.Read(sr, binary.LittleEndian, &second); err != nil {
return 0, err
}
size = int(first)&0x7F<<8 | int(second)
} else {
size = int(first)
}
return size, nil
}
// parseVar16Len reads a variable-length length for UTF-16 strings.
//
// Strings in UTF-16 format have length indicated by a length encoded in the stored data. It is
// either 1 or 2 characters of length data. This allows a maximum length of 0x7FFFFFF (2147483647
// bytes), but if you're storing that much data in a string, you're abusing them.
//
// If the high bit is set, then there are two characters or 4 bytes of length data encoded. In that
// case, drop the high bit of the first character and add it together with the next character.
func parseVar16Len(sr *io.SectionReader) (int, error) {
var size int
var first, second uint16
if err := binary.Read(sr, binary.LittleEndian, &first); err != nil {
return 0, err
}
if first&0x8000 != 0 { // high bit is set, read next two bytes
if err := binary.Read(sr, binary.LittleEndian, &second); err != nil {
return 0, err
}
size = int(first)&0x7FFF<<16 | int(second)
} else {
size = int(first)
}
return size, nil
}