capacitor-dex-editor 0.0.69 → 0.0.71

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.
Files changed (40) hide show
  1. package/android/build.gradle +22 -0
  2. package/android/src/main/cpp/CMakeLists.txt +57 -0
  3. package/android/src/main/cpp/apk/apk_handler.cpp +121 -0
  4. package/android/src/main/cpp/apk/zip_utils.cpp +425 -0
  5. package/android/src/main/cpp/arsc/arsc_parser.cpp +390 -0
  6. package/android/src/main/cpp/dex/dex_builder.cpp +752 -0
  7. package/android/src/main/cpp/dex/dex_parser.cpp +620 -0
  8. package/android/src/main/cpp/dex/smali_disasm.cpp +1223 -0
  9. package/android/src/main/cpp/dex/smali_to_java.cpp +576 -0
  10. package/android/src/main/cpp/include/apk/apk_handler.h +41 -0
  11. package/android/src/main/cpp/include/apk/zip_utils.h +57 -0
  12. package/android/src/main/cpp/include/arsc/arsc_parser.h +98 -0
  13. package/android/src/main/cpp/include/dex/dex_builder.h +189 -0
  14. package/android/src/main/cpp/include/dex/dex_parser.h +137 -0
  15. package/android/src/main/cpp/include/dex/smali_disasm.h +127 -0
  16. package/android/src/main/cpp/include/dex/smali_to_java.h +50 -0
  17. package/android/src/main/cpp/include/xml/android_resources.h +495 -0
  18. package/android/src/main/cpp/include/xml/axml_parser.h +147 -0
  19. package/android/src/main/cpp/jni_bridge.cpp +872 -0
  20. package/android/src/main/cpp/third_party/miniz.c +646 -0
  21. package/android/src/main/cpp/third_party/miniz.h +605 -0
  22. package/android/src/main/cpp/third_party/miniz_common.h +97 -0
  23. package/android/src/main/cpp/third_party/miniz_export.h +6 -0
  24. package/android/src/main/cpp/third_party/miniz_tdef.c +1597 -0
  25. package/android/src/main/cpp/third_party/miniz_tdef.h +199 -0
  26. package/android/src/main/cpp/third_party/miniz_tinfl.c +770 -0
  27. package/android/src/main/cpp/third_party/miniz_tinfl.h +150 -0
  28. package/android/src/main/cpp/third_party/miniz_zip.c +4895 -0
  29. package/android/src/main/cpp/third_party/miniz_zip.h +454 -0
  30. package/android/src/main/cpp/third_party/nlohmann_json/CMakeLists.txt +0 -0
  31. package/android/src/main/cpp/third_party/nlohmann_json/single_include/nlohmann/json.hpp +24765 -0
  32. package/android/src/main/cpp/xml/axml_parser.cpp +1701 -0
  33. package/android/src/main/java/com/aetherlink/dexeditor/CppDex.java +295 -0
  34. package/android/src/main/java/com/aetherlink/dexeditor/DexManager.java +146 -50
  35. package/package.json +1 -1
  36. package/android/src/main/java/com/aetherlink/dexeditor/RustDex.java +0 -203
  37. package/android/src/main/jniLibs/arm64-v8a/libdex_rust.so +0 -0
  38. package/android/src/main/jniLibs/armeabi-v7a/libdex_rust.so +0 -0
  39. package/android/src/main/jniLibs/x86/libdex_rust.so +0 -0
  40. package/android/src/main/jniLibs/x86_64/libdex_rust.so +0 -0
@@ -0,0 +1,752 @@
1
+ #include "dex/dex_builder.h"
2
+ #include <fstream>
3
+ #include <algorithm>
4
+ #include <cstring>
5
+ #include <sstream>
6
+
7
+ namespace dex {
8
+
9
+ template<typename T>
10
+ static void write_le(std::vector<uint8_t>& out, T val) {
11
+ for (size_t i = 0; i < sizeof(T); i++) {
12
+ out.push_back(static_cast<uint8_t>(val >> (i * 8)));
13
+ }
14
+ }
15
+
16
+ template<typename T>
17
+ static T read_le(const uint8_t* p) {
18
+ T val = 0;
19
+ for (size_t i = 0; i < sizeof(T); i++) {
20
+ val |= static_cast<T>(p[i]) << (i * 8);
21
+ }
22
+ return val;
23
+ }
24
+
25
+ // Prototype implementation
26
+ std::string Prototype::to_string() const {
27
+ std::string result = "(";
28
+ for (const auto& p : param_types) {
29
+ result += p;
30
+ }
31
+ result += ")" + return_type;
32
+ return result;
33
+ }
34
+
35
+ // ClassBuilder implementation
36
+ ClassBuilder& ClassBuilder::add_field(const std::string& name, const std::string& type, uint32_t flags) {
37
+ instance_fields.push_back({name, type, flags});
38
+ return *this;
39
+ }
40
+
41
+ ClassBuilder& ClassBuilder::add_static_field(const std::string& name, const std::string& type, uint32_t flags) {
42
+ static_fields.push_back({name, type, flags});
43
+ return *this;
44
+ }
45
+
46
+ ClassBuilder& ClassBuilder::add_method(const MethodDef& method) {
47
+ if (method.access_flags & (ACC_STATIC | ACC_PRIVATE | ACC_CONSTRUCTOR)) {
48
+ direct_methods.push_back(method);
49
+ } else {
50
+ virtual_methods.push_back(method);
51
+ }
52
+ return *this;
53
+ }
54
+
55
+ MethodDef& ClassBuilder::create_method(const std::string& name, const Prototype& proto, uint32_t flags) {
56
+ MethodDef method;
57
+ method.name = name;
58
+ method.prototype = proto;
59
+ method.access_flags = flags;
60
+ method.registers_size = 1;
61
+ method.ins_size = 0;
62
+ method.outs_size = 0;
63
+
64
+ if (flags & (ACC_STATIC | ACC_PRIVATE | ACC_CONSTRUCTOR)) {
65
+ direct_methods.push_back(method);
66
+ return direct_methods.back();
67
+ } else {
68
+ virtual_methods.push_back(method);
69
+ return virtual_methods.back();
70
+ }
71
+ }
72
+
73
+ // DexBuilder implementation
74
+ DexBuilder::DexBuilder() {
75
+ // Add default strings that are commonly needed
76
+ }
77
+
78
+ bool DexBuilder::load(const std::vector<uint8_t>& data) {
79
+ if (data.size() < sizeof(DexHeader)) return false;
80
+
81
+ DexHeader header;
82
+ std::memcpy(&header, data.data(), sizeof(DexHeader));
83
+
84
+ if (std::memcmp(header.magic, "dex\n", 4) != 0) return false;
85
+
86
+ original_data_ = data;
87
+ has_original_ = true;
88
+
89
+ // Parse string pool
90
+ for (uint32_t i = 0; i < header.string_ids_size; i++) {
91
+ uint32_t str_off = read_le<uint32_t>(&data[header.string_ids_off + i * 4]);
92
+
93
+ // Read ULEB128 length
94
+ size_t pos = str_off;
95
+ uint32_t len = 0;
96
+ int shift = 0;
97
+ while (pos < data.size()) {
98
+ uint8_t b = data[pos++];
99
+ len |= (b & 0x7F) << shift;
100
+ if ((b & 0x80) == 0) break;
101
+ shift += 7;
102
+ }
103
+
104
+ std::string str(reinterpret_cast<const char*>(&data[pos]), len);
105
+ strings_.push_back(str);
106
+ string_map_[str] = i;
107
+ }
108
+
109
+ // Parse type pool
110
+ for (uint32_t i = 0; i < header.type_ids_size; i++) {
111
+ uint32_t str_idx = read_le<uint32_t>(&data[header.type_ids_off + i * 4]);
112
+ if (str_idx < strings_.size()) {
113
+ types_.push_back(strings_[str_idx]);
114
+ type_map_[strings_[str_idx]] = i;
115
+ }
116
+ }
117
+
118
+ // Parse proto pool
119
+ for (uint32_t i = 0; i < header.proto_ids_size; i++) {
120
+ size_t off = header.proto_ids_off + i * 12;
121
+ ProtoId proto;
122
+ proto.shorty_idx = read_le<uint32_t>(&data[off]);
123
+ proto.return_type_idx = read_le<uint32_t>(&data[off + 4]);
124
+ uint32_t params_off = read_le<uint32_t>(&data[off + 8]);
125
+
126
+ if (params_off != 0 && params_off + 4 <= data.size()) {
127
+ uint32_t param_count = read_le<uint32_t>(&data[params_off]);
128
+ for (uint32_t j = 0; j < param_count && params_off + 4 + (j + 1) * 2 <= data.size(); j++) {
129
+ uint16_t type_idx = read_le<uint16_t>(&data[params_off + 4 + j * 2]);
130
+ proto.param_type_idxs.push_back(type_idx);
131
+ }
132
+ }
133
+
134
+ protos_.push_back(proto);
135
+
136
+ // Build proto string for map
137
+ std::string proto_str = "(";
138
+ for (auto idx : proto.param_type_idxs) {
139
+ if (idx < types_.size()) proto_str += types_[idx];
140
+ }
141
+ proto_str += ")";
142
+ if (proto.return_type_idx < types_.size()) {
143
+ proto_str += types_[proto.return_type_idx];
144
+ }
145
+ proto_map_[proto_str] = i;
146
+ }
147
+
148
+ // Parse field pool
149
+ for (uint32_t i = 0; i < header.field_ids_size; i++) {
150
+ size_t off = header.field_ids_off + i * 8;
151
+ FieldId field;
152
+ field.class_idx = read_le<uint16_t>(&data[off]);
153
+ field.type_idx = read_le<uint16_t>(&data[off + 2]);
154
+ field.name_idx = read_le<uint32_t>(&data[off + 4]);
155
+ fields_.push_back(field);
156
+
157
+ std::string field_key;
158
+ if (field.class_idx < types_.size()) field_key += types_[field.class_idx];
159
+ field_key += "->";
160
+ if (field.name_idx < strings_.size()) field_key += strings_[field.name_idx];
161
+ field_key += ":";
162
+ if (field.type_idx < types_.size()) field_key += types_[field.type_idx];
163
+ field_map_[field_key] = i;
164
+ }
165
+
166
+ // Parse method pool
167
+ for (uint32_t i = 0; i < header.method_ids_size; i++) {
168
+ size_t off = header.method_ids_off + i * 8;
169
+ MethodId method;
170
+ method.class_idx = read_le<uint16_t>(&data[off]);
171
+ method.proto_idx = read_le<uint16_t>(&data[off + 2]);
172
+ method.name_idx = read_le<uint32_t>(&data[off + 4]);
173
+ methods_.push_back(method);
174
+
175
+ std::string method_key;
176
+ if (method.class_idx < types_.size()) method_key += types_[method.class_idx];
177
+ method_key += "->";
178
+ if (method.name_idx < strings_.size()) method_key += strings_[method.name_idx];
179
+ if (method.proto_idx < protos_.size()) {
180
+ const auto& proto = protos_[method.proto_idx];
181
+ method_key += "(";
182
+ for (auto idx : proto.param_type_idxs) {
183
+ if (idx < types_.size()) method_key += types_[idx];
184
+ }
185
+ method_key += ")";
186
+ if (proto.return_type_idx < types_.size()) {
187
+ method_key += types_[proto.return_type_idx];
188
+ }
189
+ }
190
+ method_map_[method_key] = i;
191
+ }
192
+
193
+ return true;
194
+ }
195
+
196
+ bool DexBuilder::load(const std::string& path) {
197
+ std::ifstream file(path, std::ios::binary);
198
+ if (!file) return false;
199
+
200
+ file.seekg(0, std::ios::end);
201
+ size_t size = file.tellg();
202
+ file.seekg(0, std::ios::beg);
203
+
204
+ std::vector<uint8_t> data(size);
205
+ file.read(reinterpret_cast<char*>(data.data()), size);
206
+
207
+ return load(data);
208
+ }
209
+
210
+ ClassBuilder& DexBuilder::make_class(const std::string& class_name) {
211
+ auto it = class_map_.find(class_name);
212
+ if (it != class_map_.end()) {
213
+ return classes_[it->second];
214
+ }
215
+
216
+ classes_.emplace_back(class_name);
217
+ class_map_[class_name] = classes_.size() - 1;
218
+
219
+ // Ensure class type is in type pool
220
+ get_or_add_type(class_name);
221
+
222
+ return classes_.back();
223
+ }
224
+
225
+ ClassBuilder* DexBuilder::get_class(const std::string& class_name) {
226
+ auto it = class_map_.find(class_name);
227
+ if (it != class_map_.end()) {
228
+ return &classes_[it->second];
229
+ }
230
+ return nullptr;
231
+ }
232
+
233
+ bool DexBuilder::add_method(const std::string& class_name, const MethodDef& method) {
234
+ ClassBuilder* cls = get_class(class_name);
235
+ if (!cls) {
236
+ cls = &make_class(class_name);
237
+ }
238
+ cls->add_method(method);
239
+ return true;
240
+ }
241
+
242
+ bool DexBuilder::modify_method(const std::string& class_name, const std::string& method_name,
243
+ const std::string& new_prototype, const std::vector<uint8_t>& new_code) {
244
+ ClassBuilder* cls = get_class(class_name);
245
+ if (!cls) return false;
246
+
247
+ // Find and modify method
248
+ for (auto& m : cls->direct_methods) {
249
+ if (m.name == method_name) {
250
+ // Parse new prototype
251
+ // Format: (params)return
252
+ size_t paren_end = new_prototype.find(')');
253
+ if (paren_end != std::string::npos) {
254
+ m.prototype.return_type = new_prototype.substr(paren_end + 1);
255
+ m.prototype.param_types.clear();
256
+
257
+ std::string params = new_prototype.substr(1, paren_end - 1);
258
+ size_t i = 0;
259
+ while (i < params.size()) {
260
+ if (params[i] == 'L') {
261
+ size_t end = params.find(';', i);
262
+ if (end != std::string::npos) {
263
+ m.prototype.param_types.push_back(params.substr(i, end - i + 1));
264
+ i = end + 1;
265
+ } else break;
266
+ } else if (params[i] == '[') {
267
+ size_t start = i;
268
+ while (i < params.size() && params[i] == '[') i++;
269
+ if (i < params.size()) {
270
+ if (params[i] == 'L') {
271
+ size_t end = params.find(';', i);
272
+ if (end != std::string::npos) {
273
+ m.prototype.param_types.push_back(params.substr(start, end - start + 1));
274
+ i = end + 1;
275
+ } else break;
276
+ } else {
277
+ m.prototype.param_types.push_back(params.substr(start, i - start + 1));
278
+ i++;
279
+ }
280
+ }
281
+ } else {
282
+ m.prototype.param_types.push_back(std::string(1, params[i]));
283
+ i++;
284
+ }
285
+ }
286
+ }
287
+ m.code = new_code;
288
+ return true;
289
+ }
290
+ }
291
+
292
+ for (auto& m : cls->virtual_methods) {
293
+ if (m.name == method_name) {
294
+ // Same logic as above
295
+ size_t paren_end = new_prototype.find(')');
296
+ if (paren_end != std::string::npos) {
297
+ m.prototype.return_type = new_prototype.substr(paren_end + 1);
298
+ m.prototype.param_types.clear();
299
+ // ... (same parsing logic)
300
+ }
301
+ m.code = new_code;
302
+ return true;
303
+ }
304
+ }
305
+
306
+ return false;
307
+ }
308
+
309
+ uint32_t DexBuilder::get_or_add_string(const std::string& str) {
310
+ auto it = string_map_.find(str);
311
+ if (it != string_map_.end()) {
312
+ return it->second;
313
+ }
314
+
315
+ uint32_t idx = strings_.size();
316
+ strings_.push_back(str);
317
+ string_map_[str] = idx;
318
+ return idx;
319
+ }
320
+
321
+ uint32_t DexBuilder::get_or_add_type(const std::string& type) {
322
+ auto it = type_map_.find(type);
323
+ if (it != type_map_.end()) {
324
+ return it->second;
325
+ }
326
+
327
+ get_or_add_string(type);
328
+
329
+ uint32_t idx = types_.size();
330
+ types_.push_back(type);
331
+ type_map_[type] = idx;
332
+ return idx;
333
+ }
334
+
335
+ uint32_t DexBuilder::get_or_add_proto(const Prototype& proto) {
336
+ std::string proto_str = proto.to_string();
337
+ auto it = proto_map_.find(proto_str);
338
+ if (it != proto_map_.end()) {
339
+ return it->second;
340
+ }
341
+
342
+ ProtoId pid;
343
+ pid.shorty_idx = get_or_add_string(get_shorty(proto));
344
+ pid.return_type_idx = get_or_add_type(proto.return_type);
345
+ for (const auto& p : proto.param_types) {
346
+ pid.param_type_idxs.push_back(get_or_add_type(p));
347
+ }
348
+
349
+ uint32_t idx = protos_.size();
350
+ protos_.push_back(pid);
351
+ proto_map_[proto_str] = idx;
352
+ return idx;
353
+ }
354
+
355
+ uint32_t DexBuilder::get_or_add_field(const std::string& class_name, const std::string& field_name, const std::string& type) {
356
+ std::string key = class_name + "->" + field_name + ":" + type;
357
+ auto it = field_map_.find(key);
358
+ if (it != field_map_.end()) {
359
+ return it->second;
360
+ }
361
+
362
+ FieldId fid;
363
+ fid.class_idx = get_or_add_type(class_name);
364
+ fid.type_idx = get_or_add_type(type);
365
+ fid.name_idx = get_or_add_string(field_name);
366
+
367
+ uint32_t idx = fields_.size();
368
+ fields_.push_back(fid);
369
+ field_map_[key] = idx;
370
+ return idx;
371
+ }
372
+
373
+ uint32_t DexBuilder::get_or_add_method(const std::string& class_name, const std::string& method_name, const Prototype& proto) {
374
+ std::string key = class_name + "->" + method_name + proto.to_string();
375
+ auto it = method_map_.find(key);
376
+ if (it != method_map_.end()) {
377
+ return it->second;
378
+ }
379
+
380
+ MethodId mid;
381
+ mid.class_idx = get_or_add_type(class_name);
382
+ mid.proto_idx = get_or_add_proto(proto);
383
+ mid.name_idx = get_or_add_string(method_name);
384
+
385
+ uint32_t idx = methods_.size();
386
+ methods_.push_back(mid);
387
+ method_map_[key] = idx;
388
+ return idx;
389
+ }
390
+
391
+ std::string DexBuilder::get_shorty(const Prototype& proto) const {
392
+ std::string shorty;
393
+
394
+ // Return type shorty
395
+ if (proto.return_type.empty() || proto.return_type == "V") {
396
+ shorty += 'V';
397
+ } else if (proto.return_type[0] == 'L' || proto.return_type[0] == '[') {
398
+ shorty += 'L';
399
+ } else {
400
+ shorty += proto.return_type[0];
401
+ }
402
+
403
+ // Param shorty
404
+ for (const auto& p : proto.param_types) {
405
+ if (p[0] == 'L' || p[0] == '[') {
406
+ shorty += 'L';
407
+ } else {
408
+ shorty += p[0];
409
+ }
410
+ }
411
+
412
+ return shorty;
413
+ }
414
+
415
+ void DexBuilder::write_uleb128(std::vector<uint8_t>& out, uint32_t value) {
416
+ do {
417
+ uint8_t b = value & 0x7F;
418
+ value >>= 7;
419
+ if (value != 0) b |= 0x80;
420
+ out.push_back(b);
421
+ } while (value != 0);
422
+ }
423
+
424
+ void DexBuilder::write_sleb128(std::vector<uint8_t>& out, int32_t value) {
425
+ bool more = true;
426
+ while (more) {
427
+ uint8_t b = value & 0x7F;
428
+ value >>= 7;
429
+ if ((value == 0 && (b & 0x40) == 0) || (value == -1 && (b & 0x40) != 0)) {
430
+ more = false;
431
+ } else {
432
+ b |= 0x80;
433
+ }
434
+ out.push_back(b);
435
+ }
436
+ }
437
+
438
+ std::vector<uint8_t> DexBuilder::build_code_item(const MethodDef& method) {
439
+ std::vector<uint8_t> out;
440
+
441
+ write_le<uint16_t>(out, method.registers_size);
442
+ write_le<uint16_t>(out, method.ins_size);
443
+ write_le<uint16_t>(out, method.outs_size);
444
+ write_le<uint16_t>(out, 0); // tries_size
445
+ write_le<uint32_t>(out, 0); // debug_info_off
446
+ write_le<uint32_t>(out, method.code.size() / 2); // insns_size in 16-bit units
447
+
448
+ out.insert(out.end(), method.code.begin(), method.code.end());
449
+
450
+ // Align to 4 bytes
451
+ while (out.size() % 4 != 0) {
452
+ out.push_back(0);
453
+ }
454
+
455
+ return out;
456
+ }
457
+
458
+ std::vector<uint8_t> DexBuilder::build_class_data(const ClassBuilder& cls) {
459
+ std::vector<uint8_t> out;
460
+
461
+ write_uleb128(out, cls.static_fields.size());
462
+ write_uleb128(out, cls.instance_fields.size());
463
+ write_uleb128(out, cls.direct_methods.size());
464
+ write_uleb128(out, cls.virtual_methods.size());
465
+
466
+ // Static fields (field_idx_diff, access_flags)
467
+ uint32_t prev_idx = 0;
468
+ for (const auto& f : cls.static_fields) {
469
+ uint32_t idx = get_or_add_field(cls.class_name, f.name, f.type);
470
+ write_uleb128(out, idx - prev_idx);
471
+ write_uleb128(out, f.access_flags);
472
+ prev_idx = idx;
473
+ }
474
+
475
+ // Instance fields
476
+ prev_idx = 0;
477
+ for (const auto& f : cls.instance_fields) {
478
+ uint32_t idx = get_or_add_field(cls.class_name, f.name, f.type);
479
+ write_uleb128(out, idx - prev_idx);
480
+ write_uleb128(out, f.access_flags);
481
+ prev_idx = idx;
482
+ }
483
+
484
+ // Note: method code offsets are placeholders, will be filled during final build
485
+ // Direct methods
486
+ prev_idx = 0;
487
+ for (const auto& m : cls.direct_methods) {
488
+ uint32_t idx = get_or_add_method(cls.class_name, m.name, m.prototype);
489
+ write_uleb128(out, idx - prev_idx);
490
+ write_uleb128(out, m.access_flags);
491
+ write_uleb128(out, 0); // code_off placeholder
492
+ prev_idx = idx;
493
+ }
494
+
495
+ // Virtual methods
496
+ prev_idx = 0;
497
+ for (const auto& m : cls.virtual_methods) {
498
+ uint32_t idx = get_or_add_method(cls.class_name, m.name, m.prototype);
499
+ write_uleb128(out, idx - prev_idx);
500
+ write_uleb128(out, m.access_flags);
501
+ write_uleb128(out, 0); // code_off placeholder
502
+ prev_idx = idx;
503
+ }
504
+
505
+ return out;
506
+ }
507
+
508
+ uint32_t DexBuilder::compute_checksum(const std::vector<uint8_t>& data) {
509
+ uint32_t s1 = 1, s2 = 0;
510
+ for (size_t i = 12; i < data.size(); i++) {
511
+ s1 = (s1 + data[i]) % 65521;
512
+ s2 = (s2 + s1) % 65521;
513
+ }
514
+ return (s2 << 16) | s1;
515
+ }
516
+
517
+ std::vector<uint8_t> DexBuilder::build() {
518
+ if (has_original_ && classes_.empty()) {
519
+ return original_data_;
520
+ }
521
+
522
+ std::vector<uint8_t> out;
523
+ out.resize(0x70, 0); // Header
524
+ std::memcpy(out.data(), "dex\n035\0", 8);
525
+
526
+ // === Data Section (variable-size items first) ===
527
+ uint32_t data_start = out.size();
528
+
529
+ // 1. String data
530
+ std::vector<uint32_t> string_data_offs;
531
+ for (const auto& s : strings_) {
532
+ string_data_offs.push_back(out.size());
533
+ write_uleb128(out, s.size());
534
+ out.insert(out.end(), s.begin(), s.end());
535
+ out.push_back(0);
536
+ }
537
+ while (out.size() % 4 != 0) out.push_back(0);
538
+
539
+ // 2. Type lists for protos
540
+ std::vector<uint32_t> type_list_offs;
541
+ for (const auto& p : protos_) {
542
+ if (p.param_type_idxs.empty()) {
543
+ type_list_offs.push_back(0);
544
+ } else {
545
+ while (out.size() % 4 != 0) out.push_back(0);
546
+ type_list_offs.push_back(out.size());
547
+ write_le<uint32_t>(out, p.param_type_idxs.size());
548
+ for (auto idx : p.param_type_idxs) {
549
+ write_le<uint16_t>(out, idx);
550
+ }
551
+ while (out.size() % 4 != 0) out.push_back(0);
552
+ }
553
+ }
554
+
555
+ // 3. Code items
556
+ std::vector<uint32_t> code_item_offs;
557
+ std::vector<std::pair<size_t, size_t>> class_method_indices; // class_idx -> (start, count)
558
+
559
+ for (size_t ci = 0; ci < classes_.size(); ci++) {
560
+ const auto& cls = classes_[ci];
561
+ size_t start = code_item_offs.size();
562
+
563
+ for (const auto& m : cls.direct_methods) {
564
+ if (m.code.empty()) {
565
+ code_item_offs.push_back(0);
566
+ } else {
567
+ while (out.size() % 4 != 0) out.push_back(0);
568
+ code_item_offs.push_back(out.size());
569
+ auto code_data = build_code_item(m);
570
+ out.insert(out.end(), code_data.begin(), code_data.end());
571
+ }
572
+ }
573
+ for (const auto& m : cls.virtual_methods) {
574
+ if (m.code.empty()) {
575
+ code_item_offs.push_back(0);
576
+ } else {
577
+ while (out.size() % 4 != 0) out.push_back(0);
578
+ code_item_offs.push_back(out.size());
579
+ auto code_data = build_code_item(m);
580
+ out.insert(out.end(), code_data.begin(), code_data.end());
581
+ }
582
+ }
583
+ class_method_indices.push_back({start, code_item_offs.size() - start});
584
+ }
585
+
586
+ // 4. Class data items
587
+ std::vector<uint32_t> class_data_offs;
588
+ size_t code_idx = 0;
589
+ for (size_t ci = 0; ci < classes_.size(); ci++) {
590
+ const auto& cls = classes_[ci];
591
+ if (cls.static_fields.empty() && cls.instance_fields.empty() &&
592
+ cls.direct_methods.empty() && cls.virtual_methods.empty()) {
593
+ class_data_offs.push_back(0);
594
+ continue;
595
+ }
596
+
597
+ class_data_offs.push_back(out.size());
598
+
599
+ write_uleb128(out, cls.static_fields.size());
600
+ write_uleb128(out, cls.instance_fields.size());
601
+ write_uleb128(out, cls.direct_methods.size());
602
+ write_uleb128(out, cls.virtual_methods.size());
603
+
604
+ // Fields
605
+ uint32_t prev_idx = 0;
606
+ for (const auto& f : cls.static_fields) {
607
+ uint32_t idx = field_map_[cls.class_name + "->" + f.name + ":" + f.type];
608
+ write_uleb128(out, idx - prev_idx);
609
+ write_uleb128(out, f.access_flags);
610
+ prev_idx = idx;
611
+ }
612
+ prev_idx = 0;
613
+ for (const auto& f : cls.instance_fields) {
614
+ uint32_t idx = field_map_[cls.class_name + "->" + f.name + ":" + f.type];
615
+ write_uleb128(out, idx - prev_idx);
616
+ write_uleb128(out, f.access_flags);
617
+ prev_idx = idx;
618
+ }
619
+
620
+ // Methods with code offsets
621
+ prev_idx = 0;
622
+ for (const auto& m : cls.direct_methods) {
623
+ uint32_t idx = method_map_[cls.class_name + "->" + m.name + m.prototype.to_string()];
624
+ write_uleb128(out, idx - prev_idx);
625
+ write_uleb128(out, m.access_flags);
626
+ write_uleb128(out, code_item_offs[code_idx++]);
627
+ prev_idx = idx;
628
+ }
629
+ prev_idx = 0;
630
+ for (const auto& m : cls.virtual_methods) {
631
+ uint32_t idx = method_map_[cls.class_name + "->" + m.name + m.prototype.to_string()];
632
+ write_uleb128(out, idx - prev_idx);
633
+ write_uleb128(out, m.access_flags);
634
+ write_uleb128(out, code_item_offs[code_idx++]);
635
+ prev_idx = idx;
636
+ }
637
+ }
638
+ while (out.size() % 4 != 0) out.push_back(0);
639
+
640
+ // === Fixed-size sections ===
641
+
642
+ // String IDs
643
+ uint32_t string_ids_off = out.size();
644
+ for (uint32_t off : string_data_offs) {
645
+ write_le<uint32_t>(out, off);
646
+ }
647
+
648
+ // Type IDs
649
+ uint32_t type_ids_off = out.size();
650
+ for (const auto& t : types_) {
651
+ auto it = string_map_.find(t);
652
+ write_le<uint32_t>(out, it != string_map_.end() ? it->second : 0);
653
+ }
654
+
655
+ // Proto IDs with type_list offsets
656
+ uint32_t proto_ids_off = out.size();
657
+ for (size_t i = 0; i < protos_.size(); i++) {
658
+ write_le<uint32_t>(out, protos_[i].shorty_idx);
659
+ write_le<uint32_t>(out, protos_[i].return_type_idx);
660
+ write_le<uint32_t>(out, type_list_offs[i]);
661
+ }
662
+
663
+ // Field IDs
664
+ uint32_t field_ids_off = out.size();
665
+ for (const auto& f : fields_) {
666
+ write_le<uint16_t>(out, f.class_idx);
667
+ write_le<uint16_t>(out, f.type_idx);
668
+ write_le<uint32_t>(out, f.name_idx);
669
+ }
670
+
671
+ // Method IDs
672
+ uint32_t method_ids_off = out.size();
673
+ for (const auto& m : methods_) {
674
+ write_le<uint16_t>(out, m.class_idx);
675
+ write_le<uint16_t>(out, m.proto_idx);
676
+ write_le<uint32_t>(out, m.name_idx);
677
+ }
678
+
679
+ // Class defs
680
+ uint32_t class_defs_off = out.size();
681
+ for (size_t i = 0; i < classes_.size(); i++) {
682
+ const auto& cls = classes_[i];
683
+ auto it = type_map_.find(cls.class_name);
684
+ write_le<uint32_t>(out, it != type_map_.end() ? it->second : 0);
685
+ write_le<uint32_t>(out, cls.access_flags);
686
+ auto super_it = type_map_.find(cls.super_class);
687
+ write_le<uint32_t>(out, super_it != type_map_.end() ? super_it->second : 0xFFFFFFFF);
688
+ write_le<uint32_t>(out, 0); // interfaces_off
689
+ write_le<uint32_t>(out, 0xFFFFFFFF); // source_file_idx
690
+ write_le<uint32_t>(out, 0); // annotations_off
691
+ write_le<uint32_t>(out, class_data_offs[i]);
692
+ write_le<uint32_t>(out, 0); // static_values_off
693
+ }
694
+
695
+ // Map list
696
+ uint32_t map_off = out.size();
697
+ uint32_t map_count = 0;
698
+ std::vector<std::tuple<uint16_t, uint32_t, uint32_t>> map_items;
699
+
700
+ map_items.push_back({0x0000, 1, 0}); // header
701
+ if (!strings_.empty()) map_items.push_back({0x0001, (uint32_t)strings_.size(), string_ids_off});
702
+ if (!types_.empty()) map_items.push_back({0x0002, (uint32_t)types_.size(), type_ids_off});
703
+ if (!protos_.empty()) map_items.push_back({0x0003, (uint32_t)protos_.size(), proto_ids_off});
704
+ if (!fields_.empty()) map_items.push_back({0x0004, (uint32_t)fields_.size(), field_ids_off});
705
+ if (!methods_.empty()) map_items.push_back({0x0005, (uint32_t)methods_.size(), method_ids_off});
706
+ if (!classes_.empty()) map_items.push_back({0x0006, (uint32_t)classes_.size(), class_defs_off});
707
+ map_items.push_back({0x1000, 1, map_off}); // map_list itself
708
+
709
+ write_le<uint32_t>(out, map_items.size());
710
+ for (const auto& item : map_items) {
711
+ write_le<uint16_t>(out, std::get<0>(item));
712
+ write_le<uint16_t>(out, 0); // unused
713
+ write_le<uint32_t>(out, std::get<1>(item));
714
+ write_le<uint32_t>(out, std::get<2>(item));
715
+ }
716
+
717
+ // Update header
718
+ DexHeader* header = reinterpret_cast<DexHeader*>(out.data());
719
+ header->file_size = out.size();
720
+ header->header_size = 0x70;
721
+ header->endian_tag = 0x12345678;
722
+ header->map_off = map_off;
723
+ header->string_ids_size = strings_.size();
724
+ header->string_ids_off = strings_.empty() ? 0 : string_ids_off;
725
+ header->type_ids_size = types_.size();
726
+ header->type_ids_off = types_.empty() ? 0 : type_ids_off;
727
+ header->proto_ids_size = protos_.size();
728
+ header->proto_ids_off = protos_.empty() ? 0 : proto_ids_off;
729
+ header->field_ids_size = fields_.size();
730
+ header->field_ids_off = fields_.empty() ? 0 : field_ids_off;
731
+ header->method_ids_size = methods_.size();
732
+ header->method_ids_off = methods_.empty() ? 0 : method_ids_off;
733
+ header->class_defs_size = classes_.size();
734
+ header->class_defs_off = classes_.empty() ? 0 : class_defs_off;
735
+ header->data_size = out.size() - data_start;
736
+ header->data_off = data_start;
737
+
738
+ // Compute checksum
739
+ header->checksum = compute_checksum(out);
740
+
741
+ return out;
742
+ }
743
+
744
+ bool DexBuilder::save(const std::string& path) {
745
+ auto data = build();
746
+ std::ofstream file(path, std::ios::binary);
747
+ if (!file) return false;
748
+ file.write(reinterpret_cast<const char*>(data.data()), data.size());
749
+ return true;
750
+ }
751
+
752
+ } // namespace dex