capacitor-dex-editor 0.0.68 → 0.0.70
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.
- package/android/build.gradle +22 -0
- package/android/src/main/cpp/CMakeLists.txt +57 -0
- package/android/src/main/cpp/apk/apk_handler.cpp +121 -0
- package/android/src/main/cpp/apk/zip_utils.cpp +425 -0
- package/android/src/main/cpp/arsc/arsc_parser.cpp +390 -0
- package/android/src/main/cpp/dex/dex_builder.cpp +752 -0
- package/android/src/main/cpp/dex/dex_parser.cpp +620 -0
- package/android/src/main/cpp/dex/smali_disasm.cpp +1223 -0
- package/android/src/main/cpp/dex/smali_to_java.cpp +576 -0
- package/android/src/main/cpp/include/apk/apk_handler.h +41 -0
- package/android/src/main/cpp/include/apk/zip_utils.h +57 -0
- package/android/src/main/cpp/include/arsc/arsc_parser.h +98 -0
- package/android/src/main/cpp/include/dex/dex_builder.h +189 -0
- package/android/src/main/cpp/include/dex/dex_parser.h +137 -0
- package/android/src/main/cpp/include/dex/smali_disasm.h +127 -0
- package/android/src/main/cpp/include/dex/smali_to_java.h +50 -0
- package/android/src/main/cpp/include/xml/android_resources.h +495 -0
- package/android/src/main/cpp/include/xml/axml_parser.h +147 -0
- package/android/src/main/cpp/jni_bridge.cpp +872 -0
- package/android/src/main/cpp/third_party/miniz.c +646 -0
- package/android/src/main/cpp/third_party/miniz.h +605 -0
- package/android/src/main/cpp/third_party/miniz_common.h +97 -0
- package/android/src/main/cpp/third_party/miniz_export.h +6 -0
- package/android/src/main/cpp/third_party/miniz_tdef.c +1597 -0
- package/android/src/main/cpp/third_party/miniz_tdef.h +199 -0
- package/android/src/main/cpp/third_party/miniz_tinfl.c +770 -0
- package/android/src/main/cpp/third_party/miniz_tinfl.h +150 -0
- package/android/src/main/cpp/third_party/miniz_zip.c +4895 -0
- package/android/src/main/cpp/third_party/miniz_zip.h +454 -0
- package/android/src/main/cpp/third_party/nlohmann_json/CMakeLists.txt +0 -0
- package/android/src/main/cpp/third_party/nlohmann_json/single_include/nlohmann/json.hpp +24765 -0
- package/android/src/main/cpp/xml/axml_parser.cpp +1701 -0
- package/android/src/main/java/com/aetherlink/dexeditor/CppDex.java +295 -0
- package/android/src/main/java/com/aetherlink/dexeditor/DexManager.java +20 -20
- package/package.json +1 -1
- package/android/src/main/java/com/aetherlink/dexeditor/RustDex.java +0 -108
- package/android/src/main/jniLibs/arm64-v8a/libdex_rust.so +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libdex_rust.so +0 -0
- package/android/src/main/jniLibs/x86/libdex_rust.so +0 -0
- package/android/src/main/jniLibs/x86_64/libdex_rust.so +0 -0
|
@@ -0,0 +1,1701 @@
|
|
|
1
|
+
#include "xml/axml_parser.h"
|
|
2
|
+
#include <sstream>
|
|
3
|
+
#include <cstring>
|
|
4
|
+
#include <functional>
|
|
5
|
+
|
|
6
|
+
namespace axml {
|
|
7
|
+
|
|
8
|
+
static const uint16_t RES_NULL_TYPE = 0x0000;
|
|
9
|
+
static const uint16_t RES_STRING_POOL_TYPE = 0x0001;
|
|
10
|
+
static const uint16_t RES_TABLE_TYPE = 0x0002;
|
|
11
|
+
static const uint16_t RES_XML_TYPE = 0x0003;
|
|
12
|
+
static const uint16_t RES_XML_START_NAMESPACE_TYPE = 0x0100;
|
|
13
|
+
static const uint16_t RES_XML_END_NAMESPACE_TYPE = 0x0101;
|
|
14
|
+
static const uint16_t RES_XML_START_ELEMENT_TYPE = 0x0102;
|
|
15
|
+
static const uint16_t RES_XML_END_ELEMENT_TYPE = 0x0103;
|
|
16
|
+
static const uint16_t RES_XML_CDATA_TYPE = 0x0104;
|
|
17
|
+
static const uint16_t RES_XML_RESOURCE_MAP_TYPE = 0x0180;
|
|
18
|
+
|
|
19
|
+
// 类型常量直接使用 android_resources.h 中定义的 ResourceValueType 枚举
|
|
20
|
+
|
|
21
|
+
template<typename T>
|
|
22
|
+
static T read_le(const uint8_t* p) {
|
|
23
|
+
T val = 0;
|
|
24
|
+
for (size_t i = 0; i < sizeof(T); i++) {
|
|
25
|
+
val |= static_cast<T>(p[i]) << (i * 8);
|
|
26
|
+
}
|
|
27
|
+
return val;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
template<typename T>
|
|
31
|
+
static void write_le(uint8_t* p, T val) {
|
|
32
|
+
for (size_t i = 0; i < sizeof(T); i++) {
|
|
33
|
+
p[i] = static_cast<uint8_t>(val >> (i * 8));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
bool AxmlParser::parse(const std::vector<uint8_t>& data) {
|
|
38
|
+
data_ = data;
|
|
39
|
+
|
|
40
|
+
if (data_.size() < 8) return false;
|
|
41
|
+
|
|
42
|
+
uint16_t type = read_le<uint16_t>(&data_[0]);
|
|
43
|
+
uint16_t header_size = read_le<uint16_t>(&data_[2]);
|
|
44
|
+
uint32_t file_size = read_le<uint32_t>(&data_[4]);
|
|
45
|
+
|
|
46
|
+
if (type != RES_XML_TYPE) return false;
|
|
47
|
+
if (file_size > data_.size()) return false;
|
|
48
|
+
|
|
49
|
+
size_t offset = header_size;
|
|
50
|
+
|
|
51
|
+
while (offset < data_.size()) {
|
|
52
|
+
if (offset + 8 > data_.size()) break;
|
|
53
|
+
|
|
54
|
+
uint16_t chunk_type = read_le<uint16_t>(&data_[offset]);
|
|
55
|
+
uint16_t chunk_header_size = read_le<uint16_t>(&data_[offset + 2]);
|
|
56
|
+
uint32_t chunk_size = read_le<uint32_t>(&data_[offset + 4]);
|
|
57
|
+
|
|
58
|
+
if (chunk_size == 0 || offset + chunk_size > data_.size()) break;
|
|
59
|
+
|
|
60
|
+
switch (chunk_type) {
|
|
61
|
+
case RES_STRING_POOL_TYPE:
|
|
62
|
+
parse_string_pool(offset);
|
|
63
|
+
break;
|
|
64
|
+
case RES_XML_RESOURCE_MAP_TYPE:
|
|
65
|
+
parse_resource_map(offset);
|
|
66
|
+
break;
|
|
67
|
+
case RES_XML_START_ELEMENT_TYPE:
|
|
68
|
+
case RES_XML_END_ELEMENT_TYPE:
|
|
69
|
+
case RES_XML_START_NAMESPACE_TYPE:
|
|
70
|
+
case RES_XML_END_NAMESPACE_TYPE:
|
|
71
|
+
case RES_XML_CDATA_TYPE:
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
offset += chunk_size;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
parse_elements(offset);
|
|
79
|
+
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
bool AxmlParser::parse_string_pool(size_t& offset) {
|
|
84
|
+
if (offset + 28 > data_.size()) return false;
|
|
85
|
+
|
|
86
|
+
uint32_t string_count = read_le<uint32_t>(&data_[offset + 8]);
|
|
87
|
+
uint32_t style_count = read_le<uint32_t>(&data_[offset + 12]);
|
|
88
|
+
uint32_t flags = read_le<uint32_t>(&data_[offset + 16]);
|
|
89
|
+
uint32_t strings_start = read_le<uint32_t>(&data_[offset + 20]);
|
|
90
|
+
uint32_t styles_start = read_le<uint32_t>(&data_[offset + 24]);
|
|
91
|
+
|
|
92
|
+
bool is_utf8 = (flags & (1 << 8)) != 0;
|
|
93
|
+
|
|
94
|
+
string_pool_.clear();
|
|
95
|
+
string_pool_.reserve(string_count);
|
|
96
|
+
|
|
97
|
+
size_t offsets_start = offset + 28;
|
|
98
|
+
|
|
99
|
+
for (uint32_t i = 0; i < string_count; i++) {
|
|
100
|
+
uint32_t string_offset = read_le<uint32_t>(&data_[offsets_start + i * 4]);
|
|
101
|
+
size_t str_pos = offset + strings_start + string_offset;
|
|
102
|
+
|
|
103
|
+
if (str_pos >= data_.size()) {
|
|
104
|
+
string_pool_.push_back("");
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
std::string str;
|
|
109
|
+
if (is_utf8) {
|
|
110
|
+
uint8_t len1 = data_[str_pos];
|
|
111
|
+
str_pos++;
|
|
112
|
+
if (len1 & 0x80) str_pos++;
|
|
113
|
+
|
|
114
|
+
uint8_t len2 = data_[str_pos];
|
|
115
|
+
str_pos++;
|
|
116
|
+
if (len2 & 0x80) {
|
|
117
|
+
len2 = ((len2 & 0x7F) << 8) | data_[str_pos];
|
|
118
|
+
str_pos++;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (str_pos + len2 <= data_.size()) {
|
|
122
|
+
str = std::string(reinterpret_cast<const char*>(&data_[str_pos]), len2);
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
uint16_t len = read_le<uint16_t>(&data_[str_pos]);
|
|
126
|
+
str_pos += 2;
|
|
127
|
+
if (len & 0x8000) {
|
|
128
|
+
len = ((len & 0x7FFF) << 16) | read_le<uint16_t>(&data_[str_pos]);
|
|
129
|
+
str_pos += 2;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
for (uint16_t j = 0; j < len && str_pos + 2 <= data_.size(); j++) {
|
|
133
|
+
uint16_t ch = read_le<uint16_t>(&data_[str_pos]);
|
|
134
|
+
str_pos += 2;
|
|
135
|
+
if (ch < 0x80) {
|
|
136
|
+
str += static_cast<char>(ch);
|
|
137
|
+
} else if (ch < 0x800) {
|
|
138
|
+
str += static_cast<char>(0xC0 | (ch >> 6));
|
|
139
|
+
str += static_cast<char>(0x80 | (ch & 0x3F));
|
|
140
|
+
} else {
|
|
141
|
+
str += static_cast<char>(0xE0 | (ch >> 12));
|
|
142
|
+
str += static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
|
|
143
|
+
str += static_cast<char>(0x80 | (ch & 0x3F));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
string_pool_.push_back(str);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
bool AxmlParser::parse_resource_map(size_t& offset) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
bool AxmlParser::parse_elements(size_t& offset) {
|
|
159
|
+
std::vector<Element*> element_stack;
|
|
160
|
+
|
|
161
|
+
size_t pos = 8;
|
|
162
|
+
|
|
163
|
+
while (pos < data_.size()) {
|
|
164
|
+
if (pos + 8 > data_.size()) break;
|
|
165
|
+
|
|
166
|
+
uint16_t chunk_type = read_le<uint16_t>(&data_[pos]);
|
|
167
|
+
uint16_t chunk_header_size = read_le<uint16_t>(&data_[pos + 2]);
|
|
168
|
+
uint32_t chunk_size = read_le<uint32_t>(&data_[pos + 4]);
|
|
169
|
+
|
|
170
|
+
if (chunk_size == 0 || pos + chunk_size > data_.size()) break;
|
|
171
|
+
|
|
172
|
+
if (chunk_type == RES_XML_START_ELEMENT_TYPE) {
|
|
173
|
+
if (pos + 28 > data_.size()) break;
|
|
174
|
+
|
|
175
|
+
uint32_t ns_idx = read_le<uint32_t>(&data_[pos + 16]);
|
|
176
|
+
uint32_t name_idx = read_le<uint32_t>(&data_[pos + 20]);
|
|
177
|
+
uint16_t attr_start = read_le<uint16_t>(&data_[pos + 24]);
|
|
178
|
+
uint16_t attr_size = read_le<uint16_t>(&data_[pos + 26]);
|
|
179
|
+
uint16_t attr_count = read_le<uint16_t>(&data_[pos + 28]);
|
|
180
|
+
|
|
181
|
+
Element elem;
|
|
182
|
+
if (ns_idx != 0xFFFFFFFF && ns_idx < string_pool_.size()) {
|
|
183
|
+
elem.namespace_uri = string_pool_[ns_idx];
|
|
184
|
+
}
|
|
185
|
+
if (name_idx < string_pool_.size()) {
|
|
186
|
+
elem.name = string_pool_[name_idx];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Attributes start after: chunk header (16) + attr_start offset
|
|
190
|
+
size_t attr_pos = pos + 16 + attr_start;
|
|
191
|
+
for (uint16_t i = 0; i < attr_count && attr_pos + 20 <= data_.size(); i++) {
|
|
192
|
+
Attribute attr;
|
|
193
|
+
|
|
194
|
+
uint32_t attr_ns = read_le<uint32_t>(&data_[attr_pos]);
|
|
195
|
+
uint32_t attr_name = read_le<uint32_t>(&data_[attr_pos + 4]);
|
|
196
|
+
uint32_t attr_raw = read_le<uint32_t>(&data_[attr_pos + 8]);
|
|
197
|
+
uint16_t attr_type = read_le<uint16_t>(&data_[attr_pos + 14]);
|
|
198
|
+
uint32_t attr_data = read_le<uint32_t>(&data_[attr_pos + 16]);
|
|
199
|
+
|
|
200
|
+
if (attr_ns != 0xFFFFFFFF && attr_ns < string_pool_.size()) {
|
|
201
|
+
attr.namespace_uri = string_pool_[attr_ns];
|
|
202
|
+
}
|
|
203
|
+
if (attr_name < string_pool_.size()) {
|
|
204
|
+
attr.name = string_pool_[attr_name];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
attr.type = attr_type >> 8;
|
|
208
|
+
attr.data = attr_data;
|
|
209
|
+
|
|
210
|
+
if (attr_raw != 0xFFFFFFFF && attr_raw < string_pool_.size()) {
|
|
211
|
+
attr.value = string_pool_[attr_raw];
|
|
212
|
+
} else {
|
|
213
|
+
switch (attr.type) {
|
|
214
|
+
case ResourceValueType::TYPE_STRING:
|
|
215
|
+
if (attr_data < string_pool_.size()) {
|
|
216
|
+
attr.value = string_pool_[attr_data];
|
|
217
|
+
}
|
|
218
|
+
break;
|
|
219
|
+
case ResourceValueType::TYPE_INT_DEC:
|
|
220
|
+
attr.value = std::to_string(static_cast<int32_t>(attr_data));
|
|
221
|
+
break;
|
|
222
|
+
case ResourceValueType::TYPE_INT_HEX: {
|
|
223
|
+
std::ostringstream ss;
|
|
224
|
+
ss << "0x" << std::hex << attr_data;
|
|
225
|
+
attr.value = ss.str();
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
case ResourceValueType::TYPE_INT_BOOLEAN:
|
|
229
|
+
attr.value = attr_data ? "true" : "false";
|
|
230
|
+
break;
|
|
231
|
+
case ResourceValueType::TYPE_REFERENCE: {
|
|
232
|
+
std::ostringstream ss;
|
|
233
|
+
ss << "@0x" << std::hex << attr_data;
|
|
234
|
+
attr.value = ss.str();
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
case ResourceValueType::TYPE_ATTRIBUTE: {
|
|
238
|
+
std::ostringstream ss;
|
|
239
|
+
ss << "?0x" << std::hex << attr_data;
|
|
240
|
+
attr.value = ss.str();
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
case ResourceValueType::TYPE_DIMENSION:
|
|
244
|
+
attr.value = parse_complex_value(attr_data, false);
|
|
245
|
+
break;
|
|
246
|
+
case ResourceValueType::TYPE_FRACTION:
|
|
247
|
+
attr.value = parse_complex_value(attr_data, true);
|
|
248
|
+
break;
|
|
249
|
+
case ResourceValueType::TYPE_FLOAT: {
|
|
250
|
+
float f;
|
|
251
|
+
std::memcpy(&f, &attr_data, sizeof(f));
|
|
252
|
+
attr.value = std::to_string(f);
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
case ResourceValueType::TYPE_INT_COLOR_ARGB8:
|
|
256
|
+
case ResourceValueType::TYPE_INT_COLOR_RGB8:
|
|
257
|
+
case ResourceValueType::TYPE_INT_COLOR_ARGB4:
|
|
258
|
+
case ResourceValueType::TYPE_INT_COLOR_RGB4:
|
|
259
|
+
attr.value = format_color(attr_data, attr.type);
|
|
260
|
+
break;
|
|
261
|
+
default:
|
|
262
|
+
attr.value = std::to_string(attr_data);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
elem.attributes.push_back(attr);
|
|
268
|
+
attr_pos += attr_size > 0 ? attr_size : 20;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (element_stack.empty()) {
|
|
272
|
+
root_ = elem;
|
|
273
|
+
element_stack.push_back(&root_);
|
|
274
|
+
} else {
|
|
275
|
+
element_stack.back()->children.push_back(elem);
|
|
276
|
+
element_stack.push_back(&element_stack.back()->children.back());
|
|
277
|
+
}
|
|
278
|
+
} else if (chunk_type == RES_XML_END_ELEMENT_TYPE) {
|
|
279
|
+
if (!element_stack.empty()) {
|
|
280
|
+
element_stack.pop_back();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
pos += chunk_size;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
std::string AxmlParser::to_xml(int indent) const {
|
|
291
|
+
return element_to_xml(root_, indent);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
std::string AxmlParser::element_to_xml(const Element& elem, int indent) const {
|
|
295
|
+
std::stringstream ss;
|
|
296
|
+
std::string ind(indent * 2, ' ');
|
|
297
|
+
|
|
298
|
+
ss << ind << "<" << elem.name;
|
|
299
|
+
|
|
300
|
+
for (const auto& attr : elem.attributes) {
|
|
301
|
+
ss << " ";
|
|
302
|
+
if (!attr.namespace_uri.empty()) {
|
|
303
|
+
size_t pos = attr.namespace_uri.rfind('/');
|
|
304
|
+
if (pos != std::string::npos) {
|
|
305
|
+
ss << attr.namespace_uri.substr(pos + 1) << ":";
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
ss << attr.name << "=\"" << attr.value << "\"";
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (elem.children.empty() && elem.text.empty()) {
|
|
312
|
+
ss << "/>\n";
|
|
313
|
+
} else {
|
|
314
|
+
ss << ">\n";
|
|
315
|
+
|
|
316
|
+
for (const auto& child : elem.children) {
|
|
317
|
+
ss << element_to_xml(child, indent + 1);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (!elem.text.empty()) {
|
|
321
|
+
ss << ind << " " << elem.text << "\n";
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
ss << ind << "</" << elem.name << ">\n";
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return ss.str();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
std::string AxmlParser::get_attribute_value(const Element& elem, const std::string& name) const {
|
|
331
|
+
for (const auto& attr : elem.attributes) {
|
|
332
|
+
if (attr.name == name) {
|
|
333
|
+
return attr.value;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return "";
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
std::string AxmlParser::get_package_name() const {
|
|
340
|
+
return get_attribute_value(root_, "package");
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
std::string AxmlParser::get_version_name() const {
|
|
344
|
+
return get_attribute_value(root_, "versionName");
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
int AxmlParser::get_version_code() const {
|
|
348
|
+
std::string val = get_attribute_value(root_, "versionCode");
|
|
349
|
+
return val.empty() ? 0 : std::stoi(val);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
std::string AxmlParser::get_min_sdk() const {
|
|
353
|
+
for (const auto& child : root_.children) {
|
|
354
|
+
if (child.name == "uses-sdk") {
|
|
355
|
+
return get_attribute_value(child, "minSdkVersion");
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return "";
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
std::string AxmlParser::get_target_sdk() const {
|
|
362
|
+
for (const auto& child : root_.children) {
|
|
363
|
+
if (child.name == "uses-sdk") {
|
|
364
|
+
return get_attribute_value(child, "targetSdkVersion");
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return "";
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
std::vector<std::string> AxmlParser::get_permissions() const {
|
|
371
|
+
std::vector<std::string> perms;
|
|
372
|
+
for (const auto& child : root_.children) {
|
|
373
|
+
if (child.name == "uses-permission") {
|
|
374
|
+
std::string name = get_attribute_value(child, "name");
|
|
375
|
+
if (!name.empty()) {
|
|
376
|
+
perms.push_back(name);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return perms;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
std::vector<std::string> AxmlParser::get_activities() const {
|
|
384
|
+
std::vector<std::string> activities;
|
|
385
|
+
for (const auto& child : root_.children) {
|
|
386
|
+
if (child.name == "application") {
|
|
387
|
+
for (const auto& app_child : child.children) {
|
|
388
|
+
if (app_child.name == "activity") {
|
|
389
|
+
std::string name = get_attribute_value(app_child, "name");
|
|
390
|
+
if (!name.empty()) {
|
|
391
|
+
activities.push_back(name);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return activities;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
std::vector<std::string> AxmlParser::get_services() const {
|
|
401
|
+
std::vector<std::string> services;
|
|
402
|
+
for (const auto& child : root_.children) {
|
|
403
|
+
if (child.name == "application") {
|
|
404
|
+
for (const auto& app_child : child.children) {
|
|
405
|
+
if (app_child.name == "service") {
|
|
406
|
+
std::string name = get_attribute_value(app_child, "name");
|
|
407
|
+
if (!name.empty()) {
|
|
408
|
+
services.push_back(name);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return services;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
std::vector<std::string> AxmlParser::get_receivers() const {
|
|
418
|
+
std::vector<std::string> receivers;
|
|
419
|
+
for (const auto& child : root_.children) {
|
|
420
|
+
if (child.name == "application") {
|
|
421
|
+
for (const auto& app_child : child.children) {
|
|
422
|
+
if (app_child.name == "receiver") {
|
|
423
|
+
std::string name = get_attribute_value(app_child, "name");
|
|
424
|
+
if (!name.empty()) {
|
|
425
|
+
receivers.push_back(name);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return receivers;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
std::string AxmlParser::get_info() const {
|
|
435
|
+
std::stringstream ss;
|
|
436
|
+
|
|
437
|
+
ss << "AndroidManifest Info:\n";
|
|
438
|
+
ss << " Package: " << get_package_name() << "\n";
|
|
439
|
+
ss << " Version Name: " << get_version_name() << "\n";
|
|
440
|
+
ss << " Version Code: " << get_version_code() << "\n";
|
|
441
|
+
ss << " Min SDK: " << get_min_sdk() << "\n";
|
|
442
|
+
ss << " Target SDK: " << get_target_sdk() << "\n";
|
|
443
|
+
|
|
444
|
+
auto perms = get_permissions();
|
|
445
|
+
ss << " Permissions: " << perms.size() << "\n";
|
|
446
|
+
for (const auto& p : perms) {
|
|
447
|
+
ss << " - " << p << "\n";
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
auto activities = get_activities();
|
|
451
|
+
ss << " Activities: " << activities.size() << "\n";
|
|
452
|
+
|
|
453
|
+
auto services = get_services();
|
|
454
|
+
ss << " Services: " << services.size() << "\n";
|
|
455
|
+
|
|
456
|
+
auto receivers = get_receivers();
|
|
457
|
+
ss << " Receivers: " << receivers.size() << "\n";
|
|
458
|
+
|
|
459
|
+
return ss.str();
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
bool AxmlEditor::load(const std::vector<uint8_t>& data) {
|
|
463
|
+
data_ = data;
|
|
464
|
+
return parse_internal();
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
bool AxmlEditor::parse_internal() {
|
|
468
|
+
if (data_.size() < 8) return false;
|
|
469
|
+
|
|
470
|
+
uint16_t type = read_le<uint16_t>(&data_[0]);
|
|
471
|
+
if (type != 0x0003) return false;
|
|
472
|
+
|
|
473
|
+
size_t offset = 8;
|
|
474
|
+
string_pool_.clear();
|
|
475
|
+
|
|
476
|
+
while (offset < data_.size()) {
|
|
477
|
+
if (offset + 8 > data_.size()) break;
|
|
478
|
+
|
|
479
|
+
uint16_t chunk_type = read_le<uint16_t>(&data_[offset]);
|
|
480
|
+
uint32_t chunk_size = read_le<uint32_t>(&data_[offset + 4]);
|
|
481
|
+
|
|
482
|
+
if (chunk_size == 0 || offset + chunk_size > data_.size()) break;
|
|
483
|
+
|
|
484
|
+
if (chunk_type == 0x0001) {
|
|
485
|
+
chunk_info_.string_pool_offset = offset;
|
|
486
|
+
chunk_info_.string_pool_size = chunk_size;
|
|
487
|
+
|
|
488
|
+
uint32_t string_count = read_le<uint32_t>(&data_[offset + 8]);
|
|
489
|
+
uint32_t flags = read_le<uint32_t>(&data_[offset + 16]);
|
|
490
|
+
uint32_t strings_start = read_le<uint32_t>(&data_[offset + 20]);
|
|
491
|
+
bool is_utf8 = (flags & (1 << 8)) != 0;
|
|
492
|
+
|
|
493
|
+
size_t offsets_start = offset + 28;
|
|
494
|
+
|
|
495
|
+
for (uint32_t i = 0; i < string_count; i++) {
|
|
496
|
+
uint32_t str_offset = read_le<uint32_t>(&data_[offsets_start + i * 4]);
|
|
497
|
+
size_t str_pos = offset + strings_start + str_offset;
|
|
498
|
+
|
|
499
|
+
std::string str;
|
|
500
|
+
if (str_pos < data_.size()) {
|
|
501
|
+
if (is_utf8) {
|
|
502
|
+
str_pos++;
|
|
503
|
+
if (str_pos < data_.size() && (data_[str_pos - 1] & 0x80)) str_pos++;
|
|
504
|
+
uint8_t len = data_[str_pos++];
|
|
505
|
+
if (len & 0x80) {
|
|
506
|
+
len = ((len & 0x7F) << 8) | data_[str_pos++];
|
|
507
|
+
}
|
|
508
|
+
if (str_pos + len <= data_.size()) {
|
|
509
|
+
str = std::string(reinterpret_cast<const char*>(&data_[str_pos]), len);
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
uint16_t len = read_le<uint16_t>(&data_[str_pos]);
|
|
513
|
+
str_pos += 2;
|
|
514
|
+
for (uint16_t j = 0; j < len && str_pos + 2 <= data_.size(); j++) {
|
|
515
|
+
uint16_t ch = read_le<uint16_t>(&data_[str_pos]);
|
|
516
|
+
str_pos += 2;
|
|
517
|
+
if (ch < 0x80) str += static_cast<char>(ch);
|
|
518
|
+
else if (ch < 0x800) {
|
|
519
|
+
str += static_cast<char>(0xC0 | (ch >> 6));
|
|
520
|
+
str += static_cast<char>(0x80 | (ch & 0x3F));
|
|
521
|
+
} else {
|
|
522
|
+
str += static_cast<char>(0xE0 | (ch >> 12));
|
|
523
|
+
str += static_cast<char>(0x80 | ((ch >> 6) & 0x3F));
|
|
524
|
+
str += static_cast<char>(0x80 | (ch & 0x3F));
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
string_pool_.push_back(str);
|
|
530
|
+
}
|
|
531
|
+
} else if (chunk_type == 0x0180) {
|
|
532
|
+
chunk_info_.resource_map_offset = offset;
|
|
533
|
+
chunk_info_.resource_map_size = chunk_size;
|
|
534
|
+
// 解析资源ID映射表
|
|
535
|
+
resource_ids_.clear();
|
|
536
|
+
size_t res_count = (chunk_size - 8) / 4;
|
|
537
|
+
for (size_t i = 0; i < res_count && offset + 8 + i * 4 + 4 <= data_.size(); i++) {
|
|
538
|
+
uint32_t res_id = read_le<uint32_t>(&data_[offset + 8 + i * 4]);
|
|
539
|
+
resource_ids_.push_back(res_id);
|
|
540
|
+
}
|
|
541
|
+
} else if (chunk_type == 0x0102) {
|
|
542
|
+
chunk_info_.xml_content_offset = offset;
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
offset += chunk_size;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
AxmlParser parser;
|
|
550
|
+
if (parser.parse(data_)) {
|
|
551
|
+
root_ = parser.root();
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
return false;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
std::vector<uint8_t> AxmlEditor::save() {
|
|
558
|
+
rebuild_binary();
|
|
559
|
+
return data_;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
void AxmlEditor::search_element(const Element& elem, const std::string& path, int& index,
|
|
563
|
+
const std::string& attr_name, const std::string& value_pattern,
|
|
564
|
+
std::vector<SearchResult>& results) const {
|
|
565
|
+
std::string current_path = path.empty() ? elem.name : path + "/" + elem.name;
|
|
566
|
+
|
|
567
|
+
for (const auto& attr : elem.attributes) {
|
|
568
|
+
bool match = false;
|
|
569
|
+
if (!attr_name.empty() && !value_pattern.empty()) {
|
|
570
|
+
match = (attr.name.find(attr_name) != std::string::npos) &&
|
|
571
|
+
(attr.value.find(value_pattern) != std::string::npos);
|
|
572
|
+
} else if (!attr_name.empty()) {
|
|
573
|
+
match = attr.name.find(attr_name) != std::string::npos;
|
|
574
|
+
} else if (!value_pattern.empty()) {
|
|
575
|
+
match = attr.value.find(value_pattern) != std::string::npos;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (match) {
|
|
579
|
+
SearchResult r;
|
|
580
|
+
r.element_path = current_path;
|
|
581
|
+
r.element_name = elem.name;
|
|
582
|
+
r.attribute_name = attr.name;
|
|
583
|
+
r.attribute_value = attr.value;
|
|
584
|
+
r.element_index = index;
|
|
585
|
+
results.push_back(r);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
index++;
|
|
590
|
+
|
|
591
|
+
for (const auto& child : elem.children) {
|
|
592
|
+
search_element(child, current_path, index, attr_name, value_pattern, results);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
std::vector<SearchResult> AxmlEditor::search_by_attribute(const std::string& attr_name, const std::string& value_pattern) const {
|
|
597
|
+
std::vector<SearchResult> results;
|
|
598
|
+
int index = 0;
|
|
599
|
+
search_element(root_, "", index, attr_name, value_pattern, results);
|
|
600
|
+
return results;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
std::vector<SearchResult> AxmlEditor::search_by_element(const std::string& element_name) const {
|
|
604
|
+
std::vector<SearchResult> results;
|
|
605
|
+
|
|
606
|
+
std::function<void(const Element&, const std::string&, int&)> search_fn;
|
|
607
|
+
search_fn = [&](const Element& elem, const std::string& path, int& index) {
|
|
608
|
+
std::string current_path = path.empty() ? elem.name : path + "/" + elem.name;
|
|
609
|
+
|
|
610
|
+
if (elem.name.find(element_name) != std::string::npos) {
|
|
611
|
+
for (const auto& attr : elem.attributes) {
|
|
612
|
+
SearchResult r;
|
|
613
|
+
r.element_path = current_path;
|
|
614
|
+
r.element_name = elem.name;
|
|
615
|
+
r.attribute_name = attr.name;
|
|
616
|
+
r.attribute_value = attr.value;
|
|
617
|
+
r.element_index = index;
|
|
618
|
+
results.push_back(r);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
index++;
|
|
623
|
+
for (const auto& child : elem.children) {
|
|
624
|
+
search_fn(child, current_path, index);
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
int index = 0;
|
|
629
|
+
search_fn(root_, "", index);
|
|
630
|
+
return results;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
std::vector<SearchResult> AxmlEditor::search_by_value(const std::string& value_pattern) const {
|
|
634
|
+
return search_by_attribute("", value_pattern);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
bool AxmlEditor::set_attribute(const std::string& element_path, const std::string& attr_name, const std::string& new_value) {
|
|
638
|
+
// 智能解析 new_value 的类型
|
|
639
|
+
bool is_int = false;
|
|
640
|
+
int32_t int_value = 0;
|
|
641
|
+
bool is_bool = false;
|
|
642
|
+
bool bool_value = false;
|
|
643
|
+
bool is_color = false;
|
|
644
|
+
uint32_t color_data = 0;
|
|
645
|
+
uint8_t color_type = 0;
|
|
646
|
+
bool is_dimension = false;
|
|
647
|
+
bool is_fraction = false;
|
|
648
|
+
uint32_t complex_data = 0;
|
|
649
|
+
|
|
650
|
+
// 检查布尔值
|
|
651
|
+
if (new_value == "true" || new_value == "false") {
|
|
652
|
+
is_bool = true;
|
|
653
|
+
bool_value = (new_value == "true");
|
|
654
|
+
}
|
|
655
|
+
// 检查颜色值 #RRGGBB 或 #AARRGGBB
|
|
656
|
+
else if (parse_color_string(new_value, color_data, color_type)) {
|
|
657
|
+
is_color = true;
|
|
658
|
+
}
|
|
659
|
+
// 检查尺寸值 (dp, sp, px, pt, in, mm)
|
|
660
|
+
else if (encode_complex_value(new_value, complex_data, is_dimension)) {
|
|
661
|
+
is_fraction = !is_dimension;
|
|
662
|
+
}
|
|
663
|
+
// 检查整数
|
|
664
|
+
else {
|
|
665
|
+
try {
|
|
666
|
+
size_t pos;
|
|
667
|
+
long long parsed = std::stoll(new_value, &pos);
|
|
668
|
+
if (pos == new_value.length()) {
|
|
669
|
+
is_int = true;
|
|
670
|
+
int_value = static_cast<int32_t>(parsed);
|
|
671
|
+
}
|
|
672
|
+
} catch (...) {
|
|
673
|
+
is_int = false;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// 记录原始字符串池大小
|
|
678
|
+
size_t original_pool_size = string_pool_.size();
|
|
679
|
+
int new_string_idx = -1;
|
|
680
|
+
|
|
681
|
+
// 对于字符串类型的值,需要添加到字符串池
|
|
682
|
+
if (!is_int) {
|
|
683
|
+
new_string_idx = find_or_add_string(new_value);
|
|
684
|
+
|
|
685
|
+
// 如果添加了新字符串,需要重建字符串池
|
|
686
|
+
bool need_rebuild = (string_pool_.size() > original_pool_size);
|
|
687
|
+
if (need_rebuild) {
|
|
688
|
+
rebuild_binary();
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
size_t offset = chunk_info_.xml_content_offset;
|
|
693
|
+
|
|
694
|
+
while (offset < data_.size()) {
|
|
695
|
+
if (offset + 8 > data_.size()) break;
|
|
696
|
+
|
|
697
|
+
uint16_t chunk_type = read_le<uint16_t>(&data_[offset]);
|
|
698
|
+
uint32_t chunk_size = read_le<uint32_t>(&data_[offset + 4]);
|
|
699
|
+
|
|
700
|
+
if (chunk_size == 0 || offset + chunk_size > data_.size()) break;
|
|
701
|
+
|
|
702
|
+
if (chunk_type == 0x0102) {
|
|
703
|
+
uint32_t name_idx = read_le<uint32_t>(&data_[offset + 20]);
|
|
704
|
+
std::string elem_name = (name_idx < string_pool_.size()) ? string_pool_[name_idx] : "";
|
|
705
|
+
|
|
706
|
+
// 元素匹配:精确匹配或路径包含元素名(元素名不能为空)
|
|
707
|
+
bool elem_match = element_path.empty() ||
|
|
708
|
+
(elem_name == element_path) ||
|
|
709
|
+
(!elem_name.empty() && element_path.find(elem_name) != std::string::npos);
|
|
710
|
+
|
|
711
|
+
if (elem_match) {
|
|
712
|
+
uint16_t attr_count = read_le<uint16_t>(&data_[offset + 28]);
|
|
713
|
+
uint16_t attr_start = read_le<uint16_t>(&data_[offset + 24]);
|
|
714
|
+
uint16_t attr_size = 20;
|
|
715
|
+
|
|
716
|
+
// Attributes start after: chunk header (16) + attr_start offset
|
|
717
|
+
size_t attr_pos = offset + 16 + attr_start;
|
|
718
|
+
for (uint16_t i = 0; i < attr_count && attr_pos + 20 <= data_.size(); i++) {
|
|
719
|
+
uint32_t attr_name_idx = read_le<uint32_t>(&data_[attr_pos + 4]);
|
|
720
|
+
std::string current_attr_name = (attr_name_idx < string_pool_.size()) ? string_pool_[attr_name_idx] : "";
|
|
721
|
+
|
|
722
|
+
// 尝试通过资源ID映射获取属性名
|
|
723
|
+
std::string res_attr_name;
|
|
724
|
+
if (attr_name_idx < resource_ids_.size()) {
|
|
725
|
+
const char* res_name = get_android_attr_name(resource_ids_[attr_name_idx]);
|
|
726
|
+
if (res_name) {
|
|
727
|
+
res_attr_name = res_name;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// 匹配:字符串池名称或资源ID映射名称
|
|
732
|
+
bool name_match = (current_attr_name == attr_name) ||
|
|
733
|
+
(!res_attr_name.empty() && res_attr_name == attr_name);
|
|
734
|
+
|
|
735
|
+
if (name_match) {
|
|
736
|
+
uint16_t attr_type_field = read_le<uint16_t>(&data_[attr_pos + 14]);
|
|
737
|
+
uint8_t attr_type = attr_type_field >> 8;
|
|
738
|
+
|
|
739
|
+
// 根据原始属性类型决定如何修改
|
|
740
|
+
if (attr_type == ResourceValueType::TYPE_STRING) {
|
|
741
|
+
// 字符串类型:修改 rawValue 和 data 为新字符串索引
|
|
742
|
+
if (new_string_idx < 0) {
|
|
743
|
+
new_string_idx = find_or_add_string(new_value);
|
|
744
|
+
if (string_pool_.size() > original_pool_size) {
|
|
745
|
+
rebuild_binary();
|
|
746
|
+
return set_attribute(element_path, attr_name, new_value);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
write_le<uint32_t>(&data_[attr_pos + 8], static_cast<uint32_t>(new_string_idx));
|
|
750
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(new_string_idx));
|
|
751
|
+
} else if (attr_type == ResourceValueType::TYPE_INT_DEC || attr_type == ResourceValueType::TYPE_INT_HEX) {
|
|
752
|
+
// 整数类型
|
|
753
|
+
if (is_int) {
|
|
754
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
755
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(int_value));
|
|
756
|
+
} else if (new_value.length() > 2 && (new_value.substr(0, 2) == "0x" || new_value.substr(0, 2) == "0X")) {
|
|
757
|
+
try {
|
|
758
|
+
uint32_t hex_val = std::stoul(new_value, nullptr, 16);
|
|
759
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
760
|
+
write_le<uint32_t>(&data_[attr_pos + 16], hex_val);
|
|
761
|
+
} catch (...) {
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
} else {
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
} else if (attr_type == ResourceValueType::TYPE_INT_BOOLEAN) {
|
|
768
|
+
// 布尔类型
|
|
769
|
+
bool bval = (new_value == "true" || new_value == "1" || (is_int && int_value != 0));
|
|
770
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
771
|
+
write_le<uint32_t>(&data_[attr_pos + 16], bval ? 0xFFFFFFFF : 0);
|
|
772
|
+
} else if (attr_type == ResourceValueType::TYPE_REFERENCE || attr_type == ResourceValueType::TYPE_ATTRIBUTE) {
|
|
773
|
+
// 资源引用类型 @resource 或 ?attr
|
|
774
|
+
if (is_int) {
|
|
775
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
776
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(int_value));
|
|
777
|
+
} else if (!new_value.empty() && (new_value[0] == '@' || new_value[0] == '?')) {
|
|
778
|
+
try {
|
|
779
|
+
uint32_t res_id = std::stoul(new_value.substr(1), nullptr, 0);
|
|
780
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
781
|
+
write_le<uint32_t>(&data_[attr_pos + 16], res_id);
|
|
782
|
+
} catch (...) {
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
} else {
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
} else if (attr_type == ResourceValueType::TYPE_DIMENSION) {
|
|
789
|
+
// 尺寸类型 (dp, sp, px, pt, in, mm)
|
|
790
|
+
uint32_t dim_data = 0;
|
|
791
|
+
bool is_dim = false;
|
|
792
|
+
if (encode_complex_value(new_value, dim_data, is_dim)) {
|
|
793
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
794
|
+
write_le<uint32_t>(&data_[attr_pos + 16], dim_data);
|
|
795
|
+
} else if (is_int) {
|
|
796
|
+
// 整数作为像素处理
|
|
797
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
798
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(int_value) << 8);
|
|
799
|
+
} else {
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
802
|
+
} else if (attr_type == ResourceValueType::TYPE_FRACTION) {
|
|
803
|
+
// 百分比类型 (%, %p)
|
|
804
|
+
uint32_t frac_data = 0;
|
|
805
|
+
bool is_dim = false;
|
|
806
|
+
if (encode_complex_value(new_value, frac_data, is_dim)) {
|
|
807
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
808
|
+
write_le<uint32_t>(&data_[attr_pos + 16], frac_data);
|
|
809
|
+
} else {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
} else if (attr_type >= ResourceValueType::TYPE_INT_COLOR_ARGB8 && attr_type <= ResourceValueType::TYPE_INT_COLOR_RGB4) {
|
|
813
|
+
// 颜色类型
|
|
814
|
+
uint32_t clr_data = 0;
|
|
815
|
+
uint8_t clr_type = 0;
|
|
816
|
+
if (parse_color_string(new_value, clr_data, clr_type)) {
|
|
817
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
818
|
+
write_le<uint32_t>(&data_[attr_pos + 16], clr_data);
|
|
819
|
+
} else if (is_int) {
|
|
820
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
821
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(int_value));
|
|
822
|
+
} else {
|
|
823
|
+
return false;
|
|
824
|
+
}
|
|
825
|
+
} else if (attr_type == ResourceValueType::TYPE_FLOAT) {
|
|
826
|
+
// 浮点类型
|
|
827
|
+
try {
|
|
828
|
+
float fval = std::stof(new_value);
|
|
829
|
+
uint32_t fdata;
|
|
830
|
+
std::memcpy(&fdata, &fval, sizeof(fdata));
|
|
831
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
832
|
+
write_le<uint32_t>(&data_[attr_pos + 16], fdata);
|
|
833
|
+
} catch (...) {
|
|
834
|
+
return false;
|
|
835
|
+
}
|
|
836
|
+
} else {
|
|
837
|
+
// 其他类型:智能处理
|
|
838
|
+
if (is_int) {
|
|
839
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
840
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(int_value));
|
|
841
|
+
} else {
|
|
842
|
+
if (new_string_idx < 0) {
|
|
843
|
+
new_string_idx = find_or_add_string(new_value);
|
|
844
|
+
if (string_pool_.size() > original_pool_size) {
|
|
845
|
+
rebuild_binary();
|
|
846
|
+
return set_attribute(element_path, attr_name, new_value);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
write_le<uint32_t>(&data_[attr_pos + 8], static_cast<uint32_t>(new_string_idx));
|
|
850
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(new_string_idx));
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
parse_internal();
|
|
855
|
+
return true;
|
|
856
|
+
}
|
|
857
|
+
attr_pos += attr_size;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
offset += chunk_size;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
return false;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
bool AxmlEditor::set_attribute_by_index(int element_index, const std::string& attr_name, const std::string& new_value) {
|
|
869
|
+
// 智能解析 new_value 的类型
|
|
870
|
+
bool is_int = false;
|
|
871
|
+
int32_t int_value = 0;
|
|
872
|
+
|
|
873
|
+
// 检查整数
|
|
874
|
+
try {
|
|
875
|
+
size_t pos;
|
|
876
|
+
long long parsed = std::stoll(new_value, &pos);
|
|
877
|
+
if (pos == new_value.length()) {
|
|
878
|
+
is_int = true;
|
|
879
|
+
int_value = static_cast<int32_t>(parsed);
|
|
880
|
+
}
|
|
881
|
+
} catch (...) {
|
|
882
|
+
is_int = false;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// 记录原始字符串池大小
|
|
886
|
+
size_t original_pool_size = string_pool_.size();
|
|
887
|
+
int new_string_idx = -1;
|
|
888
|
+
|
|
889
|
+
// 对于纯字符串类型的值,需要添加到字符串池
|
|
890
|
+
if (!is_int) {
|
|
891
|
+
new_string_idx = find_or_add_string(new_value);
|
|
892
|
+
|
|
893
|
+
// 如果添加了新字符串,需要重建字符串池
|
|
894
|
+
bool need_rebuild = (string_pool_.size() > original_pool_size);
|
|
895
|
+
if (need_rebuild) {
|
|
896
|
+
rebuild_binary();
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
int current_index = 0;
|
|
901
|
+
size_t offset = chunk_info_.xml_content_offset;
|
|
902
|
+
|
|
903
|
+
while (offset < data_.size()) {
|
|
904
|
+
if (offset + 8 > data_.size()) break;
|
|
905
|
+
|
|
906
|
+
uint16_t chunk_type = read_le<uint16_t>(&data_[offset]);
|
|
907
|
+
uint32_t chunk_size = read_le<uint32_t>(&data_[offset + 4]);
|
|
908
|
+
|
|
909
|
+
if (chunk_size == 0 || offset + chunk_size > data_.size()) break;
|
|
910
|
+
|
|
911
|
+
if (chunk_type == 0x0102) {
|
|
912
|
+
if (current_index == element_index) {
|
|
913
|
+
uint16_t attr_count = read_le<uint16_t>(&data_[offset + 28]);
|
|
914
|
+
uint16_t attr_start = read_le<uint16_t>(&data_[offset + 24]);
|
|
915
|
+
uint16_t attr_size = 20;
|
|
916
|
+
|
|
917
|
+
size_t attr_pos = offset + 16 + attr_start;
|
|
918
|
+
for (uint16_t i = 0; i < attr_count && attr_pos + 20 <= data_.size(); i++) {
|
|
919
|
+
uint32_t attr_name_idx = read_le<uint32_t>(&data_[attr_pos + 4]);
|
|
920
|
+
std::string current_attr_name = (attr_name_idx < string_pool_.size()) ? string_pool_[attr_name_idx] : "";
|
|
921
|
+
|
|
922
|
+
// 尝试通过资源ID映射获取属性名
|
|
923
|
+
std::string res_attr_name;
|
|
924
|
+
if (attr_name_idx < resource_ids_.size()) {
|
|
925
|
+
const char* res_name = get_android_attr_name(resource_ids_[attr_name_idx]);
|
|
926
|
+
if (res_name) {
|
|
927
|
+
res_attr_name = res_name;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// 匹配:字符串池名称或资源ID映射名称
|
|
932
|
+
bool name_match = (current_attr_name == attr_name) ||
|
|
933
|
+
(!res_attr_name.empty() && res_attr_name == attr_name);
|
|
934
|
+
|
|
935
|
+
if (name_match) {
|
|
936
|
+
uint16_t attr_type_field = read_le<uint16_t>(&data_[attr_pos + 14]);
|
|
937
|
+
uint8_t attr_type = attr_type_field >> 8;
|
|
938
|
+
|
|
939
|
+
// 根据原始属性类型决定如何修改
|
|
940
|
+
if (attr_type == ResourceValueType::TYPE_STRING) {
|
|
941
|
+
if (new_string_idx < 0) {
|
|
942
|
+
new_string_idx = find_or_add_string(new_value);
|
|
943
|
+
if (string_pool_.size() > original_pool_size) {
|
|
944
|
+
rebuild_binary();
|
|
945
|
+
return set_attribute_by_index(element_index, attr_name, new_value);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
write_le<uint32_t>(&data_[attr_pos + 8], static_cast<uint32_t>(new_string_idx));
|
|
949
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(new_string_idx));
|
|
950
|
+
} else if (attr_type == ResourceValueType::TYPE_INT_DEC || attr_type == ResourceValueType::TYPE_INT_HEX) {
|
|
951
|
+
if (is_int) {
|
|
952
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
953
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(int_value));
|
|
954
|
+
} else if (new_value.length() > 2 && (new_value.substr(0, 2) == "0x" || new_value.substr(0, 2) == "0X")) {
|
|
955
|
+
try {
|
|
956
|
+
uint32_t hex_val = std::stoul(new_value, nullptr, 16);
|
|
957
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
958
|
+
write_le<uint32_t>(&data_[attr_pos + 16], hex_val);
|
|
959
|
+
} catch (...) {
|
|
960
|
+
return false;
|
|
961
|
+
}
|
|
962
|
+
} else {
|
|
963
|
+
return false;
|
|
964
|
+
}
|
|
965
|
+
} else if (attr_type == ResourceValueType::TYPE_INT_BOOLEAN) {
|
|
966
|
+
bool bval = (new_value == "true" || new_value == "1" || (is_int && int_value != 0));
|
|
967
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
968
|
+
write_le<uint32_t>(&data_[attr_pos + 16], bval ? 0xFFFFFFFF : 0);
|
|
969
|
+
} else if (attr_type == ResourceValueType::TYPE_REFERENCE || attr_type == ResourceValueType::TYPE_ATTRIBUTE) {
|
|
970
|
+
if (is_int) {
|
|
971
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
972
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(int_value));
|
|
973
|
+
} else if (!new_value.empty() && (new_value[0] == '@' || new_value[0] == '?')) {
|
|
974
|
+
try {
|
|
975
|
+
uint32_t res_id = std::stoul(new_value.substr(1), nullptr, 0);
|
|
976
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
977
|
+
write_le<uint32_t>(&data_[attr_pos + 16], res_id);
|
|
978
|
+
} catch (...) {
|
|
979
|
+
return false;
|
|
980
|
+
}
|
|
981
|
+
} else {
|
|
982
|
+
return false;
|
|
983
|
+
}
|
|
984
|
+
} else if (attr_type == ResourceValueType::TYPE_DIMENSION) {
|
|
985
|
+
uint32_t dim_data = 0;
|
|
986
|
+
bool is_dim = false;
|
|
987
|
+
if (encode_complex_value(new_value, dim_data, is_dim)) {
|
|
988
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
989
|
+
write_le<uint32_t>(&data_[attr_pos + 16], dim_data);
|
|
990
|
+
} else if (is_int) {
|
|
991
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
992
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(int_value) << 8);
|
|
993
|
+
} else {
|
|
994
|
+
return false;
|
|
995
|
+
}
|
|
996
|
+
} else if (attr_type == ResourceValueType::TYPE_FRACTION) {
|
|
997
|
+
uint32_t frac_data = 0;
|
|
998
|
+
bool is_dim = false;
|
|
999
|
+
if (encode_complex_value(new_value, frac_data, is_dim)) {
|
|
1000
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
1001
|
+
write_le<uint32_t>(&data_[attr_pos + 16], frac_data);
|
|
1002
|
+
} else {
|
|
1003
|
+
return false;
|
|
1004
|
+
}
|
|
1005
|
+
} else if (attr_type >= ResourceValueType::TYPE_INT_COLOR_ARGB8 && attr_type <= ResourceValueType::TYPE_INT_COLOR_RGB4) {
|
|
1006
|
+
uint32_t clr_data = 0;
|
|
1007
|
+
uint8_t clr_type = 0;
|
|
1008
|
+
if (parse_color_string(new_value, clr_data, clr_type)) {
|
|
1009
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
1010
|
+
write_le<uint32_t>(&data_[attr_pos + 16], clr_data);
|
|
1011
|
+
} else if (is_int) {
|
|
1012
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
1013
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(int_value));
|
|
1014
|
+
} else {
|
|
1015
|
+
return false;
|
|
1016
|
+
}
|
|
1017
|
+
} else if (attr_type == ResourceValueType::TYPE_FLOAT) {
|
|
1018
|
+
try {
|
|
1019
|
+
float fval = std::stof(new_value);
|
|
1020
|
+
uint32_t fdata;
|
|
1021
|
+
std::memcpy(&fdata, &fval, sizeof(fdata));
|
|
1022
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
1023
|
+
write_le<uint32_t>(&data_[attr_pos + 16], fdata);
|
|
1024
|
+
} catch (...) {
|
|
1025
|
+
return false;
|
|
1026
|
+
}
|
|
1027
|
+
} else {
|
|
1028
|
+
if (is_int) {
|
|
1029
|
+
write_le<uint32_t>(&data_[attr_pos + 8], 0xFFFFFFFF);
|
|
1030
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(int_value));
|
|
1031
|
+
} else {
|
|
1032
|
+
if (new_string_idx < 0) {
|
|
1033
|
+
new_string_idx = find_or_add_string(new_value);
|
|
1034
|
+
if (string_pool_.size() > original_pool_size) {
|
|
1035
|
+
rebuild_binary();
|
|
1036
|
+
return set_attribute_by_index(element_index, attr_name, new_value);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
write_le<uint32_t>(&data_[attr_pos + 8], static_cast<uint32_t>(new_string_idx));
|
|
1040
|
+
write_le<uint32_t>(&data_[attr_pos + 16], static_cast<uint32_t>(new_string_idx));
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
parse_internal();
|
|
1045
|
+
return true;
|
|
1046
|
+
}
|
|
1047
|
+
attr_pos += attr_size;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
current_index++;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
offset += chunk_size;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
return false;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
bool AxmlEditor::set_package_name(const std::string& name) {
|
|
1060
|
+
return set_attribute("manifest", "package", name);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
bool AxmlEditor::set_version_name(const std::string& name) {
|
|
1064
|
+
return set_attribute("manifest", "versionName", name);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
bool AxmlEditor::set_version_code(int code) {
|
|
1068
|
+
return set_attribute("manifest", "versionCode", std::to_string(code));
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
bool AxmlEditor::set_min_sdk(int sdk) {
|
|
1072
|
+
// 先搜索uses-sdk元素的索引
|
|
1073
|
+
auto results = search_by_attribute("minSdkVersion", "");
|
|
1074
|
+
for (const auto& r : results) {
|
|
1075
|
+
if (r.element_name == "uses-sdk") {
|
|
1076
|
+
return set_attribute_by_index(r.element_index, "minSdkVersion", std::to_string(sdk));
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return set_attribute("uses-sdk", "minSdkVersion", std::to_string(sdk));
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
bool AxmlEditor::set_target_sdk(int sdk) {
|
|
1083
|
+
// 先搜索uses-sdk元素的索引
|
|
1084
|
+
auto results = search_by_attribute("targetSdkVersion", "");
|
|
1085
|
+
for (const auto& r : results) {
|
|
1086
|
+
if (r.element_name == "uses-sdk") {
|
|
1087
|
+
return set_attribute_by_index(r.element_index, "targetSdkVersion", std::to_string(sdk));
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
return set_attribute("uses-sdk", "targetSdkVersion", std::to_string(sdk));
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
bool AxmlEditor::add_permission(const std::string& permission) {
|
|
1094
|
+
// Android namespace URI
|
|
1095
|
+
const std::string android_ns = "http://schemas.android.com/apk/res/android";
|
|
1096
|
+
|
|
1097
|
+
// Add required strings to string pool
|
|
1098
|
+
int name_attr_idx = find_or_add_string("name");
|
|
1099
|
+
int perm_value_idx = find_or_add_string(permission);
|
|
1100
|
+
int uses_perm_idx = find_or_add_string("uses-permission");
|
|
1101
|
+
int android_ns_idx = find_or_add_string(android_ns);
|
|
1102
|
+
|
|
1103
|
+
// Rebuild string pool after adding new strings
|
|
1104
|
+
rebuild_binary();
|
|
1105
|
+
|
|
1106
|
+
// Find the position right after <manifest> opening tag (after first RES_XML_START_ELEMENT_TYPE)
|
|
1107
|
+
size_t insert_offset = 0;
|
|
1108
|
+
size_t offset = chunk_info_.xml_content_offset;
|
|
1109
|
+
bool found_manifest = false;
|
|
1110
|
+
|
|
1111
|
+
while (offset < data_.size()) {
|
|
1112
|
+
if (offset + 8 > data_.size()) break;
|
|
1113
|
+
|
|
1114
|
+
uint16_t chunk_type = read_le<uint16_t>(&data_[offset]);
|
|
1115
|
+
uint32_t chunk_size = read_le<uint32_t>(&data_[offset + 4]);
|
|
1116
|
+
|
|
1117
|
+
if (chunk_size == 0 || offset + chunk_size > data_.size()) break;
|
|
1118
|
+
|
|
1119
|
+
if (chunk_type == 0x0102) { // RES_XML_START_ELEMENT_TYPE
|
|
1120
|
+
uint32_t name_idx = read_le<uint32_t>(&data_[offset + 20]);
|
|
1121
|
+
if (name_idx < string_pool_.size() && string_pool_[name_idx] == "manifest") {
|
|
1122
|
+
insert_offset = offset + chunk_size;
|
|
1123
|
+
found_manifest = true;
|
|
1124
|
+
break;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
offset += chunk_size;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
if (!found_manifest) return false;
|
|
1132
|
+
|
|
1133
|
+
// Create uses-permission element
|
|
1134
|
+
// Start element chunk: header(16) + extended(20) + attrs(attr_count * 20)
|
|
1135
|
+
std::vector<uint8_t> start_elem(16 + 20 + 20); // 56 bytes total, 1 attribute
|
|
1136
|
+
|
|
1137
|
+
// Chunk header
|
|
1138
|
+
write_le<uint16_t>(&start_elem[0], 0x0102); // RES_XML_START_ELEMENT_TYPE
|
|
1139
|
+
write_le<uint16_t>(&start_elem[2], 16); // header size
|
|
1140
|
+
write_le<uint32_t>(&start_elem[4], static_cast<uint32_t>(start_elem.size())); // chunk size
|
|
1141
|
+
|
|
1142
|
+
// Extended header
|
|
1143
|
+
write_le<uint32_t>(&start_elem[8], 1); // line number
|
|
1144
|
+
write_le<uint32_t>(&start_elem[12], 0xFFFFFFFF); // comment (none)
|
|
1145
|
+
|
|
1146
|
+
// Element info
|
|
1147
|
+
write_le<uint32_t>(&start_elem[16], 0xFFFFFFFF); // namespace (none)
|
|
1148
|
+
write_le<uint32_t>(&start_elem[20], uses_perm_idx); // name
|
|
1149
|
+
write_le<uint16_t>(&start_elem[24], 0x14); // attribute start (20 bytes after element start)
|
|
1150
|
+
write_le<uint16_t>(&start_elem[26], 0x14); // attribute size (20 bytes per attr)
|
|
1151
|
+
write_le<uint16_t>(&start_elem[28], 1); // attribute count
|
|
1152
|
+
write_le<uint16_t>(&start_elem[30], 0); // id index
|
|
1153
|
+
write_le<uint16_t>(&start_elem[32], 0); // class index
|
|
1154
|
+
write_le<uint16_t>(&start_elem[34], 0); // style index
|
|
1155
|
+
|
|
1156
|
+
// Attribute: android:name="permission"
|
|
1157
|
+
size_t attr_offset = 36;
|
|
1158
|
+
write_le<uint32_t>(&start_elem[attr_offset + 0], android_ns_idx); // namespace
|
|
1159
|
+
write_le<uint32_t>(&start_elem[attr_offset + 4], name_attr_idx); // name (index of "name")
|
|
1160
|
+
write_le<uint32_t>(&start_elem[attr_offset + 8], perm_value_idx); // rawValue (string index)
|
|
1161
|
+
write_le<uint16_t>(&start_elem[attr_offset + 12], 8); // size
|
|
1162
|
+
write_le<uint8_t>(&start_elem[attr_offset + 14], 0); // res0
|
|
1163
|
+
write_le<uint8_t>(&start_elem[attr_offset + 15], 0x03); // dataType: TYPE_STRING
|
|
1164
|
+
write_le<uint32_t>(&start_elem[attr_offset + 16], perm_value_idx); // data (string index)
|
|
1165
|
+
|
|
1166
|
+
// End element chunk
|
|
1167
|
+
std::vector<uint8_t> end_elem(24);
|
|
1168
|
+
write_le<uint16_t>(&end_elem[0], 0x0103); // RES_XML_END_ELEMENT_TYPE
|
|
1169
|
+
write_le<uint16_t>(&end_elem[2], 16); // header size
|
|
1170
|
+
write_le<uint32_t>(&end_elem[4], 24); // chunk size
|
|
1171
|
+
write_le<uint32_t>(&end_elem[8], 1); // line number
|
|
1172
|
+
write_le<uint32_t>(&end_elem[12], 0xFFFFFFFF); // comment
|
|
1173
|
+
write_le<uint32_t>(&end_elem[16], 0xFFFFFFFF); // namespace (none)
|
|
1174
|
+
write_le<uint32_t>(&end_elem[20], uses_perm_idx); // name
|
|
1175
|
+
|
|
1176
|
+
// Insert both chunks
|
|
1177
|
+
std::vector<uint8_t> new_data;
|
|
1178
|
+
new_data.reserve(data_.size() + start_elem.size() + end_elem.size());
|
|
1179
|
+
|
|
1180
|
+
new_data.insert(new_data.end(), data_.begin(), data_.begin() + insert_offset);
|
|
1181
|
+
new_data.insert(new_data.end(), start_elem.begin(), start_elem.end());
|
|
1182
|
+
new_data.insert(new_data.end(), end_elem.begin(), end_elem.end());
|
|
1183
|
+
new_data.insert(new_data.end(), data_.begin() + insert_offset, data_.end());
|
|
1184
|
+
|
|
1185
|
+
// Update file size in header
|
|
1186
|
+
uint32_t new_file_size = static_cast<uint32_t>(new_data.size());
|
|
1187
|
+
write_le<uint32_t>(&new_data[4], new_file_size);
|
|
1188
|
+
|
|
1189
|
+
data_ = std::move(new_data);
|
|
1190
|
+
|
|
1191
|
+
// Re-parse to update internal state
|
|
1192
|
+
parse_internal();
|
|
1193
|
+
|
|
1194
|
+
return true;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
bool AxmlEditor::remove_permission(const std::string& permission) {
|
|
1198
|
+
size_t offset = chunk_info_.xml_content_offset;
|
|
1199
|
+
size_t perm_start = 0;
|
|
1200
|
+
size_t perm_end = 0;
|
|
1201
|
+
bool found = false;
|
|
1202
|
+
|
|
1203
|
+
while (offset < data_.size()) {
|
|
1204
|
+
if (offset + 8 > data_.size()) break;
|
|
1205
|
+
|
|
1206
|
+
uint16_t chunk_type = read_le<uint16_t>(&data_[offset]);
|
|
1207
|
+
uint32_t chunk_size = read_le<uint32_t>(&data_[offset + 4]);
|
|
1208
|
+
|
|
1209
|
+
if (chunk_size == 0 || offset + chunk_size > data_.size()) break;
|
|
1210
|
+
|
|
1211
|
+
if (chunk_type == 0x0102) { // RES_XML_START_ELEMENT_TYPE
|
|
1212
|
+
uint32_t name_idx = read_le<uint32_t>(&data_[offset + 20]);
|
|
1213
|
+
if (name_idx < string_pool_.size() && string_pool_[name_idx] == "uses-permission") {
|
|
1214
|
+
// Check if this is the permission we're looking for
|
|
1215
|
+
uint16_t attr_count = read_le<uint16_t>(&data_[offset + 28]);
|
|
1216
|
+
uint16_t attr_start = read_le<uint16_t>(&data_[offset + 24]);
|
|
1217
|
+
|
|
1218
|
+
// Attributes start after: chunk header (16) + element info start + attr_start offset
|
|
1219
|
+
size_t attr_pos = offset + 16 + attr_start;
|
|
1220
|
+
for (uint16_t i = 0; i < attr_count && attr_pos + 20 <= data_.size(); i++) {
|
|
1221
|
+
uint32_t attr_name_idx = read_le<uint32_t>(&data_[attr_pos + 4]);
|
|
1222
|
+
uint32_t attr_value_idx = read_le<uint32_t>(&data_[attr_pos + 8]);
|
|
1223
|
+
|
|
1224
|
+
if (attr_name_idx < string_pool_.size() && string_pool_[attr_name_idx] == "name" &&
|
|
1225
|
+
attr_value_idx < string_pool_.size() && string_pool_[attr_value_idx] == permission) {
|
|
1226
|
+
perm_start = offset;
|
|
1227
|
+
found = true;
|
|
1228
|
+
break;
|
|
1229
|
+
}
|
|
1230
|
+
attr_pos += 20;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
if (found) {
|
|
1234
|
+
// Find the matching end element
|
|
1235
|
+
size_t search_offset = offset + chunk_size;
|
|
1236
|
+
while (search_offset < data_.size()) {
|
|
1237
|
+
if (search_offset + 8 > data_.size()) break;
|
|
1238
|
+
|
|
1239
|
+
uint16_t search_type = read_le<uint16_t>(&data_[search_offset]);
|
|
1240
|
+
uint32_t search_size = read_le<uint32_t>(&data_[search_offset + 4]);
|
|
1241
|
+
|
|
1242
|
+
if (search_type == 0x0103) { // RES_XML_END_ELEMENT_TYPE
|
|
1243
|
+
uint32_t end_name_idx = read_le<uint32_t>(&data_[search_offset + 20]);
|
|
1244
|
+
if (end_name_idx == name_idx) {
|
|
1245
|
+
perm_end = search_offset + search_size;
|
|
1246
|
+
break;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
search_offset += search_size;
|
|
1250
|
+
}
|
|
1251
|
+
break;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
offset += chunk_size;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
if (!found || perm_end <= perm_start) return false;
|
|
1260
|
+
|
|
1261
|
+
// Remove the element
|
|
1262
|
+
std::vector<uint8_t> new_data;
|
|
1263
|
+
new_data.reserve(data_.size() - (perm_end - perm_start));
|
|
1264
|
+
|
|
1265
|
+
new_data.insert(new_data.end(), data_.begin(), data_.begin() + perm_start);
|
|
1266
|
+
new_data.insert(new_data.end(), data_.begin() + perm_end, data_.end());
|
|
1267
|
+
|
|
1268
|
+
// Update file size
|
|
1269
|
+
uint32_t new_file_size = static_cast<uint32_t>(new_data.size());
|
|
1270
|
+
write_le<uint32_t>(&new_data[4], new_file_size);
|
|
1271
|
+
|
|
1272
|
+
data_ = std::move(new_data);
|
|
1273
|
+
|
|
1274
|
+
// Re-parse
|
|
1275
|
+
parse_internal();
|
|
1276
|
+
|
|
1277
|
+
return true;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
bool AxmlEditor::add_activity(const std::string& activity_name, bool exported) {
|
|
1281
|
+
const std::string android_ns = "http://schemas.android.com/apk/res/android";
|
|
1282
|
+
|
|
1283
|
+
// Add required strings
|
|
1284
|
+
int name_attr_idx = find_or_add_string("name");
|
|
1285
|
+
int exported_attr_idx = find_or_add_string("exported");
|
|
1286
|
+
int activity_value_idx = find_or_add_string(activity_name);
|
|
1287
|
+
int activity_tag_idx = find_or_add_string("activity");
|
|
1288
|
+
int application_idx = find_or_add_string("application");
|
|
1289
|
+
int android_ns_idx = find_or_add_string(android_ns);
|
|
1290
|
+
|
|
1291
|
+
// Rebuild string pool
|
|
1292
|
+
rebuild_binary();
|
|
1293
|
+
|
|
1294
|
+
// Find application element's end position (insert before </application>)
|
|
1295
|
+
size_t insert_offset = 0;
|
|
1296
|
+
size_t offset = chunk_info_.xml_content_offset;
|
|
1297
|
+
bool in_application = false;
|
|
1298
|
+
int depth = 0;
|
|
1299
|
+
|
|
1300
|
+
while (offset < data_.size()) {
|
|
1301
|
+
if (offset + 8 > data_.size()) break;
|
|
1302
|
+
|
|
1303
|
+
uint16_t chunk_type = read_le<uint16_t>(&data_[offset]);
|
|
1304
|
+
uint32_t chunk_size = read_le<uint32_t>(&data_[offset + 4]);
|
|
1305
|
+
|
|
1306
|
+
if (chunk_size == 0 || offset + chunk_size > data_.size()) break;
|
|
1307
|
+
|
|
1308
|
+
if (chunk_type == 0x0102) { // Start element
|
|
1309
|
+
uint32_t name_idx = read_le<uint32_t>(&data_[offset + 20]);
|
|
1310
|
+
if (name_idx < string_pool_.size() && string_pool_[name_idx] == "application") {
|
|
1311
|
+
in_application = true;
|
|
1312
|
+
depth = 1;
|
|
1313
|
+
} else if (in_application) {
|
|
1314
|
+
depth++;
|
|
1315
|
+
}
|
|
1316
|
+
} else if (chunk_type == 0x0103) { // End element
|
|
1317
|
+
if (in_application) {
|
|
1318
|
+
depth--;
|
|
1319
|
+
if (depth == 0) {
|
|
1320
|
+
// This is </application>
|
|
1321
|
+
insert_offset = offset;
|
|
1322
|
+
break;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
offset += chunk_size;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
if (insert_offset == 0) return false;
|
|
1331
|
+
|
|
1332
|
+
// Create activity element with 2 attributes: name and exported
|
|
1333
|
+
std::vector<uint8_t> start_elem(16 + 20 + 40); // header + ext + 2 attrs
|
|
1334
|
+
|
|
1335
|
+
write_le<uint16_t>(&start_elem[0], 0x0102);
|
|
1336
|
+
write_le<uint16_t>(&start_elem[2], 16);
|
|
1337
|
+
write_le<uint32_t>(&start_elem[4], static_cast<uint32_t>(start_elem.size()));
|
|
1338
|
+
write_le<uint32_t>(&start_elem[8], 1);
|
|
1339
|
+
write_le<uint32_t>(&start_elem[12], 0xFFFFFFFF);
|
|
1340
|
+
write_le<uint32_t>(&start_elem[16], 0xFFFFFFFF);
|
|
1341
|
+
write_le<uint32_t>(&start_elem[20], activity_tag_idx);
|
|
1342
|
+
write_le<uint16_t>(&start_elem[24], 0x14);
|
|
1343
|
+
write_le<uint16_t>(&start_elem[26], 0x14);
|
|
1344
|
+
write_le<uint16_t>(&start_elem[28], 2);
|
|
1345
|
+
write_le<uint16_t>(&start_elem[30], 0);
|
|
1346
|
+
write_le<uint16_t>(&start_elem[32], 0);
|
|
1347
|
+
write_le<uint16_t>(&start_elem[34], 0);
|
|
1348
|
+
|
|
1349
|
+
// Attr 1: android:name
|
|
1350
|
+
size_t attr_offset = 36;
|
|
1351
|
+
write_le<uint32_t>(&start_elem[attr_offset + 0], android_ns_idx);
|
|
1352
|
+
write_le<uint32_t>(&start_elem[attr_offset + 4], name_attr_idx);
|
|
1353
|
+
write_le<uint32_t>(&start_elem[attr_offset + 8], activity_value_idx);
|
|
1354
|
+
write_le<uint16_t>(&start_elem[attr_offset + 12], 8);
|
|
1355
|
+
write_le<uint8_t>(&start_elem[attr_offset + 14], 0);
|
|
1356
|
+
write_le<uint8_t>(&start_elem[attr_offset + 15], 0x03);
|
|
1357
|
+
write_le<uint32_t>(&start_elem[attr_offset + 16], activity_value_idx);
|
|
1358
|
+
|
|
1359
|
+
// Attr 2: android:exported
|
|
1360
|
+
attr_offset += 20;
|
|
1361
|
+
write_le<uint32_t>(&start_elem[attr_offset + 0], android_ns_idx);
|
|
1362
|
+
write_le<uint32_t>(&start_elem[attr_offset + 4], exported_attr_idx);
|
|
1363
|
+
write_le<uint32_t>(&start_elem[attr_offset + 8], 0xFFFFFFFF);
|
|
1364
|
+
write_le<uint16_t>(&start_elem[attr_offset + 12], 8);
|
|
1365
|
+
write_le<uint8_t>(&start_elem[attr_offset + 14], 0);
|
|
1366
|
+
write_le<uint8_t>(&start_elem[attr_offset + 15], 0x12); // TYPE_INT_BOOLEAN
|
|
1367
|
+
write_le<uint32_t>(&start_elem[attr_offset + 16], exported ? 0xFFFFFFFF : 0);
|
|
1368
|
+
|
|
1369
|
+
// End element
|
|
1370
|
+
std::vector<uint8_t> end_elem(24);
|
|
1371
|
+
write_le<uint16_t>(&end_elem[0], 0x0103);
|
|
1372
|
+
write_le<uint16_t>(&end_elem[2], 16);
|
|
1373
|
+
write_le<uint32_t>(&end_elem[4], 24);
|
|
1374
|
+
write_le<uint32_t>(&end_elem[8], 1);
|
|
1375
|
+
write_le<uint32_t>(&end_elem[12], 0xFFFFFFFF);
|
|
1376
|
+
write_le<uint32_t>(&end_elem[16], 0xFFFFFFFF);
|
|
1377
|
+
write_le<uint32_t>(&end_elem[20], activity_tag_idx);
|
|
1378
|
+
|
|
1379
|
+
// Insert
|
|
1380
|
+
std::vector<uint8_t> new_data;
|
|
1381
|
+
new_data.reserve(data_.size() + start_elem.size() + end_elem.size());
|
|
1382
|
+
new_data.insert(new_data.end(), data_.begin(), data_.begin() + insert_offset);
|
|
1383
|
+
new_data.insert(new_data.end(), start_elem.begin(), start_elem.end());
|
|
1384
|
+
new_data.insert(new_data.end(), end_elem.begin(), end_elem.end());
|
|
1385
|
+
new_data.insert(new_data.end(), data_.begin() + insert_offset, data_.end());
|
|
1386
|
+
|
|
1387
|
+
write_le<uint32_t>(&new_data[4], static_cast<uint32_t>(new_data.size()));
|
|
1388
|
+
data_ = std::move(new_data);
|
|
1389
|
+
|
|
1390
|
+
parse_internal();
|
|
1391
|
+
return true;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
bool AxmlEditor::remove_activity(const std::string& activity_name) {
|
|
1395
|
+
size_t offset = chunk_info_.xml_content_offset;
|
|
1396
|
+
size_t activity_start = 0;
|
|
1397
|
+
size_t activity_end = 0;
|
|
1398
|
+
bool found = false;
|
|
1399
|
+
int depth = 0;
|
|
1400
|
+
|
|
1401
|
+
while (offset < data_.size()) {
|
|
1402
|
+
if (offset + 8 > data_.size()) break;
|
|
1403
|
+
|
|
1404
|
+
uint16_t chunk_type = read_le<uint16_t>(&data_[offset]);
|
|
1405
|
+
uint32_t chunk_size = read_le<uint32_t>(&data_[offset + 4]);
|
|
1406
|
+
|
|
1407
|
+
if (chunk_size == 0 || offset + chunk_size > data_.size()) break;
|
|
1408
|
+
|
|
1409
|
+
if (chunk_type == 0x0102) { // Start element
|
|
1410
|
+
if (!found) {
|
|
1411
|
+
uint32_t name_idx = read_le<uint32_t>(&data_[offset + 20]);
|
|
1412
|
+
if (name_idx < string_pool_.size() && string_pool_[name_idx] == "activity") {
|
|
1413
|
+
// Check android:name attribute
|
|
1414
|
+
uint16_t attr_count = read_le<uint16_t>(&data_[offset + 28]);
|
|
1415
|
+
uint16_t attr_start = read_le<uint16_t>(&data_[offset + 24]);
|
|
1416
|
+
|
|
1417
|
+
// Attributes start after: chunk header (16) + element info start + attr_start offset
|
|
1418
|
+
size_t attr_pos = offset + 16 + attr_start;
|
|
1419
|
+
for (uint16_t i = 0; i < attr_count && attr_pos + 20 <= data_.size(); i++) {
|
|
1420
|
+
uint32_t attr_name_idx = read_le<uint32_t>(&data_[attr_pos + 4]);
|
|
1421
|
+
uint32_t attr_value_idx = read_le<uint32_t>(&data_[attr_pos + 8]);
|
|
1422
|
+
|
|
1423
|
+
if (attr_name_idx < string_pool_.size() && string_pool_[attr_name_idx] == "name" &&
|
|
1424
|
+
attr_value_idx < string_pool_.size() && string_pool_[attr_value_idx] == activity_name) {
|
|
1425
|
+
activity_start = offset;
|
|
1426
|
+
found = true;
|
|
1427
|
+
depth = 1;
|
|
1428
|
+
break;
|
|
1429
|
+
}
|
|
1430
|
+
attr_pos += 20;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
} else {
|
|
1434
|
+
depth++;
|
|
1435
|
+
}
|
|
1436
|
+
} else if (chunk_type == 0x0103) { // End element
|
|
1437
|
+
if (found) {
|
|
1438
|
+
depth--;
|
|
1439
|
+
if (depth == 0) {
|
|
1440
|
+
activity_end = offset + chunk_size;
|
|
1441
|
+
break;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
offset += chunk_size;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
if (!found || activity_end <= activity_start) return false;
|
|
1450
|
+
|
|
1451
|
+
std::vector<uint8_t> new_data;
|
|
1452
|
+
new_data.reserve(data_.size() - (activity_end - activity_start));
|
|
1453
|
+
new_data.insert(new_data.end(), data_.begin(), data_.begin() + activity_start);
|
|
1454
|
+
new_data.insert(new_data.end(), data_.begin() + activity_end, data_.end());
|
|
1455
|
+
|
|
1456
|
+
write_le<uint32_t>(&new_data[4], static_cast<uint32_t>(new_data.size()));
|
|
1457
|
+
data_ = std::move(new_data);
|
|
1458
|
+
|
|
1459
|
+
parse_internal();
|
|
1460
|
+
return true;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
bool AxmlEditor::add_element(const std::string& parent_path, const std::string& element_name,
|
|
1464
|
+
const std::vector<std::pair<std::string, std::string>>& attributes) {
|
|
1465
|
+
// Generic add element - similar logic to add_activity
|
|
1466
|
+
// This is a simplified implementation
|
|
1467
|
+
return false; // TODO: implement full generic version
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
bool AxmlEditor::remove_element(const std::string& element_path) {
|
|
1471
|
+
// Generic remove element - similar logic to remove_activity
|
|
1472
|
+
return false; // TODO: implement full generic version
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
int AxmlEditor::find_or_add_string(const std::string& str) {
|
|
1476
|
+
for (size_t i = 0; i < string_pool_.size(); i++) {
|
|
1477
|
+
if (string_pool_[i] == str) {
|
|
1478
|
+
return static_cast<int>(i);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
string_pool_.push_back(str);
|
|
1482
|
+
return static_cast<int>(string_pool_.size() - 1);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// 计算 UTF-8 字符串的 Unicode 字符数量
|
|
1486
|
+
static size_t utf8_char_count(const std::string& str) {
|
|
1487
|
+
size_t count = 0;
|
|
1488
|
+
size_t i = 0;
|
|
1489
|
+
while (i < str.length()) {
|
|
1490
|
+
uint8_t c = static_cast<uint8_t>(str[i]);
|
|
1491
|
+
if ((c & 0x80) == 0) {
|
|
1492
|
+
// ASCII: 0xxxxxxx
|
|
1493
|
+
i += 1;
|
|
1494
|
+
} else if ((c & 0xE0) == 0xC0) {
|
|
1495
|
+
// 2字节序列: 110xxxxx 10xxxxxx
|
|
1496
|
+
i += 2;
|
|
1497
|
+
} else if ((c & 0xF0) == 0xE0) {
|
|
1498
|
+
// 3字节序列: 1110xxxx 10xxxxxx 10xxxxxx
|
|
1499
|
+
i += 3;
|
|
1500
|
+
} else if ((c & 0xF8) == 0xF0) {
|
|
1501
|
+
// 4字节序列: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
|
1502
|
+
i += 4;
|
|
1503
|
+
} else {
|
|
1504
|
+
// 无效的 UTF-8,按单字节处理
|
|
1505
|
+
i += 1;
|
|
1506
|
+
}
|
|
1507
|
+
count++;
|
|
1508
|
+
}
|
|
1509
|
+
return count;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
// 将 UTF-8 字符串转换为 UTF-16LE 编码
|
|
1513
|
+
static std::vector<uint16_t> utf8_to_utf16(const std::string& str) {
|
|
1514
|
+
std::vector<uint16_t> result;
|
|
1515
|
+
size_t i = 0;
|
|
1516
|
+
while (i < str.length()) {
|
|
1517
|
+
uint32_t codepoint = 0;
|
|
1518
|
+
uint8_t c = static_cast<uint8_t>(str[i]);
|
|
1519
|
+
|
|
1520
|
+
if ((c & 0x80) == 0) {
|
|
1521
|
+
codepoint = c;
|
|
1522
|
+
i += 1;
|
|
1523
|
+
} else if ((c & 0xE0) == 0xC0) {
|
|
1524
|
+
codepoint = (c & 0x1F) << 6;
|
|
1525
|
+
if (i + 1 < str.length()) {
|
|
1526
|
+
codepoint |= (static_cast<uint8_t>(str[i + 1]) & 0x3F);
|
|
1527
|
+
}
|
|
1528
|
+
i += 2;
|
|
1529
|
+
} else if ((c & 0xF0) == 0xE0) {
|
|
1530
|
+
codepoint = (c & 0x0F) << 12;
|
|
1531
|
+
if (i + 1 < str.length()) {
|
|
1532
|
+
codepoint |= (static_cast<uint8_t>(str[i + 1]) & 0x3F) << 6;
|
|
1533
|
+
}
|
|
1534
|
+
if (i + 2 < str.length()) {
|
|
1535
|
+
codepoint |= (static_cast<uint8_t>(str[i + 2]) & 0x3F);
|
|
1536
|
+
}
|
|
1537
|
+
i += 3;
|
|
1538
|
+
} else if ((c & 0xF8) == 0xF0) {
|
|
1539
|
+
codepoint = (c & 0x07) << 18;
|
|
1540
|
+
if (i + 1 < str.length()) {
|
|
1541
|
+
codepoint |= (static_cast<uint8_t>(str[i + 1]) & 0x3F) << 12;
|
|
1542
|
+
}
|
|
1543
|
+
if (i + 2 < str.length()) {
|
|
1544
|
+
codepoint |= (static_cast<uint8_t>(str[i + 2]) & 0x3F) << 6;
|
|
1545
|
+
}
|
|
1546
|
+
if (i + 3 < str.length()) {
|
|
1547
|
+
codepoint |= (static_cast<uint8_t>(str[i + 3]) & 0x3F);
|
|
1548
|
+
}
|
|
1549
|
+
i += 4;
|
|
1550
|
+
} else {
|
|
1551
|
+
i += 1;
|
|
1552
|
+
continue;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
// 转换为 UTF-16
|
|
1556
|
+
if (codepoint <= 0xFFFF) {
|
|
1557
|
+
result.push_back(static_cast<uint16_t>(codepoint));
|
|
1558
|
+
} else {
|
|
1559
|
+
// 使用代理对表示超过 BMP 的字符
|
|
1560
|
+
codepoint -= 0x10000;
|
|
1561
|
+
result.push_back(static_cast<uint16_t>(0xD800 | (codepoint >> 10)));
|
|
1562
|
+
result.push_back(static_cast<uint16_t>(0xDC00 | (codepoint & 0x3FF)));
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
return result;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
bool AxmlEditor::rebuild_binary() {
|
|
1569
|
+
if (data_.size() < 8) return false;
|
|
1570
|
+
|
|
1571
|
+
bool is_utf8 = true;
|
|
1572
|
+
if (chunk_info_.string_pool_offset + 16 < data_.size()) {
|
|
1573
|
+
uint32_t flags = read_le<uint32_t>(&data_[chunk_info_.string_pool_offset + 16]);
|
|
1574
|
+
is_utf8 = (flags & (1 << 8)) != 0;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
std::vector<uint8_t> string_data;
|
|
1578
|
+
std::vector<uint32_t> string_offsets;
|
|
1579
|
+
|
|
1580
|
+
for (const auto& str : string_pool_) {
|
|
1581
|
+
string_offsets.push_back(static_cast<uint32_t>(string_data.size()));
|
|
1582
|
+
|
|
1583
|
+
if (is_utf8) {
|
|
1584
|
+
// UTF-8 编码格式:
|
|
1585
|
+
// - 字符长度 (1或2字节): Unicode 字符数量
|
|
1586
|
+
// - 字节长度 (1或2字节): UTF-8 字节数
|
|
1587
|
+
// - UTF-8 字符串数据
|
|
1588
|
+
// - null 终止符
|
|
1589
|
+
size_t char_len = utf8_char_count(str);
|
|
1590
|
+
size_t byte_len = str.length();
|
|
1591
|
+
|
|
1592
|
+
// 写入字符长度
|
|
1593
|
+
if (char_len < 128) {
|
|
1594
|
+
string_data.push_back(static_cast<uint8_t>(char_len));
|
|
1595
|
+
} else {
|
|
1596
|
+
string_data.push_back(static_cast<uint8_t>((char_len >> 8) | 0x80));
|
|
1597
|
+
string_data.push_back(static_cast<uint8_t>(char_len & 0xFF));
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
// 写入字节长度
|
|
1601
|
+
if (byte_len < 128) {
|
|
1602
|
+
string_data.push_back(static_cast<uint8_t>(byte_len));
|
|
1603
|
+
} else {
|
|
1604
|
+
string_data.push_back(static_cast<uint8_t>((byte_len >> 8) | 0x80));
|
|
1605
|
+
string_data.push_back(static_cast<uint8_t>(byte_len & 0xFF));
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// 写入 UTF-8 字符串数据
|
|
1609
|
+
for (char c : str) {
|
|
1610
|
+
string_data.push_back(static_cast<uint8_t>(c));
|
|
1611
|
+
}
|
|
1612
|
+
// null 终止符
|
|
1613
|
+
string_data.push_back(0);
|
|
1614
|
+
} else {
|
|
1615
|
+
// UTF-16LE 编码格式:
|
|
1616
|
+
// - 字符长度 (2或4字节): UTF-16 单元数量
|
|
1617
|
+
// - UTF-16LE 字符串数据
|
|
1618
|
+
// - null 终止符 (2字节)
|
|
1619
|
+
std::vector<uint16_t> utf16_str = utf8_to_utf16(str);
|
|
1620
|
+
uint32_t len = static_cast<uint32_t>(utf16_str.size());
|
|
1621
|
+
|
|
1622
|
+
// 写入长度
|
|
1623
|
+
if (len < 0x8000) {
|
|
1624
|
+
// 短字符串格式: 2字节长度
|
|
1625
|
+
string_data.push_back(static_cast<uint8_t>(len & 0xFF));
|
|
1626
|
+
string_data.push_back(static_cast<uint8_t>((len >> 8) & 0x7F));
|
|
1627
|
+
} else {
|
|
1628
|
+
// 长字符串格式: 4字节长度,高位设置 0x8000 标志
|
|
1629
|
+
uint16_t high = static_cast<uint16_t>((len >> 16) & 0x7FFF) | 0x8000;
|
|
1630
|
+
uint16_t low = static_cast<uint16_t>(len & 0xFFFF);
|
|
1631
|
+
string_data.push_back(static_cast<uint8_t>(high & 0xFF));
|
|
1632
|
+
string_data.push_back(static_cast<uint8_t>(high >> 8));
|
|
1633
|
+
string_data.push_back(static_cast<uint8_t>(low & 0xFF));
|
|
1634
|
+
string_data.push_back(static_cast<uint8_t>(low >> 8));
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// 写入 UTF-16LE 数据
|
|
1638
|
+
for (uint16_t ch : utf16_str) {
|
|
1639
|
+
string_data.push_back(static_cast<uint8_t>(ch & 0xFF));
|
|
1640
|
+
string_data.push_back(static_cast<uint8_t>(ch >> 8));
|
|
1641
|
+
}
|
|
1642
|
+
// null 终止符
|
|
1643
|
+
string_data.push_back(0);
|
|
1644
|
+
string_data.push_back(0);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
while (string_data.size() % 4 != 0) {
|
|
1649
|
+
string_data.push_back(0);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
uint32_t header_size = 28;
|
|
1653
|
+
uint32_t offsets_size = static_cast<uint32_t>(string_pool_.size() * 4);
|
|
1654
|
+
uint32_t strings_start = header_size + offsets_size;
|
|
1655
|
+
uint32_t new_chunk_size = strings_start + static_cast<uint32_t>(string_data.size());
|
|
1656
|
+
|
|
1657
|
+
std::vector<uint8_t> new_string_pool(new_chunk_size);
|
|
1658
|
+
|
|
1659
|
+
write_le<uint16_t>(&new_string_pool[0], 0x0001);
|
|
1660
|
+
write_le<uint16_t>(&new_string_pool[2], static_cast<uint16_t>(header_size));
|
|
1661
|
+
write_le<uint32_t>(&new_string_pool[4], new_chunk_size);
|
|
1662
|
+
write_le<uint32_t>(&new_string_pool[8], static_cast<uint32_t>(string_pool_.size()));
|
|
1663
|
+
write_le<uint32_t>(&new_string_pool[12], 0);
|
|
1664
|
+
write_le<uint32_t>(&new_string_pool[16], is_utf8 ? 0x100 : 0);
|
|
1665
|
+
write_le<uint32_t>(&new_string_pool[20], strings_start);
|
|
1666
|
+
write_le<uint32_t>(&new_string_pool[24], 0);
|
|
1667
|
+
|
|
1668
|
+
for (size_t i = 0; i < string_offsets.size(); i++) {
|
|
1669
|
+
write_le<uint32_t>(&new_string_pool[header_size + i * 4], string_offsets[i]);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
std::memcpy(&new_string_pool[strings_start], string_data.data(), string_data.size());
|
|
1673
|
+
|
|
1674
|
+
int32_t size_diff = static_cast<int32_t>(new_chunk_size) - static_cast<int32_t>(chunk_info_.string_pool_size);
|
|
1675
|
+
|
|
1676
|
+
std::vector<uint8_t> new_data;
|
|
1677
|
+
new_data.reserve(data_.size() + size_diff);
|
|
1678
|
+
|
|
1679
|
+
new_data.insert(new_data.end(), data_.begin(), data_.begin() + chunk_info_.string_pool_offset);
|
|
1680
|
+
new_data.insert(new_data.end(), new_string_pool.begin(), new_string_pool.end());
|
|
1681
|
+
new_data.insert(new_data.end(),
|
|
1682
|
+
data_.begin() + chunk_info_.string_pool_offset + chunk_info_.string_pool_size,
|
|
1683
|
+
data_.end());
|
|
1684
|
+
|
|
1685
|
+
uint32_t new_file_size = static_cast<uint32_t>(new_data.size());
|
|
1686
|
+
write_le<uint32_t>(&new_data[4], new_file_size);
|
|
1687
|
+
|
|
1688
|
+
data_ = std::move(new_data);
|
|
1689
|
+
|
|
1690
|
+
chunk_info_.string_pool_size = new_chunk_size;
|
|
1691
|
+
if (chunk_info_.resource_map_offset > chunk_info_.string_pool_offset) {
|
|
1692
|
+
chunk_info_.resource_map_offset += size_diff;
|
|
1693
|
+
}
|
|
1694
|
+
if (chunk_info_.xml_content_offset > chunk_info_.string_pool_offset) {
|
|
1695
|
+
chunk_info_.xml_content_offset += size_diff;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
return true;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
} // namespace axml
|