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.
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 +20 -20
  35. package/package.json +1 -1
  36. package/android/src/main/java/com/aetherlink/dexeditor/RustDex.java +0 -108
  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,620 @@
1
+ #include "dex/dex_parser.h"
2
+ #include <fstream>
3
+ #include <sstream>
4
+ #include <cstring>
5
+
6
+ namespace dex {
7
+
8
+ template<typename T>
9
+ static T read_le(const uint8_t* p) {
10
+ T val = 0;
11
+ for (size_t i = 0; i < sizeof(T); i++) {
12
+ val |= static_cast<T>(p[i]) << (i * 8);
13
+ }
14
+ return val;
15
+ }
16
+
17
+ bool DexParser::parse(const std::string& path) {
18
+ std::ifstream file(path, std::ios::binary);
19
+ if (!file) return false;
20
+
21
+ file.seekg(0, std::ios::end);
22
+ size_t size = file.tellg();
23
+ file.seekg(0, std::ios::beg);
24
+
25
+ data_.resize(size);
26
+ file.read(reinterpret_cast<char*>(data_.data()), size);
27
+ file.close();
28
+
29
+ return parse_header() && parse_strings() && parse_types() && parse_classes();
30
+ }
31
+
32
+ bool DexParser::parse(const std::vector<uint8_t>& data) {
33
+ data_ = data;
34
+ return parse_header() && parse_strings() && parse_types() && parse_classes();
35
+ }
36
+
37
+ bool DexParser::parse_header() {
38
+ if (data_.size() < sizeof(DexHeader)) return false;
39
+
40
+ std::memcpy(&header_, data_.data(), sizeof(DexHeader));
41
+
42
+ if (std::memcmp(header_.magic, "dex\n", 4) != 0) {
43
+ return false;
44
+ }
45
+
46
+ return true;
47
+ }
48
+
49
+ bool DexParser::parse_strings() {
50
+ if (header_.string_ids_off + header_.string_ids_size * 4 > data_.size()) {
51
+ return false;
52
+ }
53
+
54
+ string_ids_.resize(header_.string_ids_size);
55
+ strings_.resize(header_.string_ids_size);
56
+
57
+ for (uint32_t i = 0; i < header_.string_ids_size; i++) {
58
+ string_ids_[i] = read_le<uint32_t>(&data_[header_.string_ids_off + i * 4]);
59
+ strings_[i] = read_string_at(string_ids_[i]);
60
+ }
61
+
62
+ return true;
63
+ }
64
+
65
+ bool DexParser::parse_types() {
66
+ if (header_.type_ids_off + header_.type_ids_size * 4 > data_.size()) {
67
+ return false;
68
+ }
69
+
70
+ type_ids_.resize(header_.type_ids_size);
71
+ types_.resize(header_.type_ids_size);
72
+
73
+ for (uint32_t i = 0; i < header_.type_ids_size; i++) {
74
+ uint32_t string_idx = read_le<uint32_t>(&data_[header_.type_ids_off + i * 4]);
75
+ type_ids_[i] = string_idx;
76
+ if (string_idx < strings_.size()) {
77
+ types_[i] = strings_[string_idx];
78
+ }
79
+ }
80
+
81
+ return true;
82
+ }
83
+
84
+ bool DexParser::parse_classes() {
85
+ if (header_.class_defs_off + header_.class_defs_size * 32 > data_.size()) {
86
+ return false;
87
+ }
88
+
89
+ classes_.resize(header_.class_defs_size);
90
+
91
+ for (uint32_t i = 0; i < header_.class_defs_size; i++) {
92
+ size_t offset = header_.class_defs_off + i * 32;
93
+ ClassDef& cd = classes_[i];
94
+
95
+ cd.class_idx = read_le<uint32_t>(&data_[offset]);
96
+ cd.access_flags = read_le<uint32_t>(&data_[offset + 4]);
97
+ cd.superclass_idx = read_le<uint32_t>(&data_[offset + 8]);
98
+ cd.interfaces_off = read_le<uint32_t>(&data_[offset + 12]);
99
+ cd.source_file_idx = read_le<uint32_t>(&data_[offset + 16]);
100
+ cd.annotations_off = read_le<uint32_t>(&data_[offset + 20]);
101
+ cd.class_data_off = read_le<uint32_t>(&data_[offset + 24]);
102
+ cd.static_values_off = read_le<uint32_t>(&data_[offset + 28]);
103
+ }
104
+
105
+ return true;
106
+ }
107
+
108
+ std::string DexParser::read_string_at(uint32_t offset) const {
109
+ if (offset >= data_.size()) return "";
110
+
111
+ size_t pos = offset;
112
+ uint32_t len = read_uleb128(pos);
113
+
114
+ if (pos + len > data_.size()) return "";
115
+
116
+ return std::string(reinterpret_cast<const char*>(&data_[pos]), len);
117
+ }
118
+
119
+ uint32_t DexParser::read_uleb128(size_t& offset) const {
120
+ uint32_t result = 0;
121
+ int shift = 0;
122
+
123
+ while (offset < data_.size()) {
124
+ uint8_t b = data_[offset++];
125
+ result |= (b & 0x7F) << shift;
126
+ if ((b & 0x80) == 0) break;
127
+ shift += 7;
128
+ }
129
+
130
+ return result;
131
+ }
132
+
133
+ std::string DexParser::get_class_name(uint32_t idx) const {
134
+ if (idx < types_.size()) {
135
+ return types_[idx];
136
+ }
137
+ return "";
138
+ }
139
+
140
+ std::vector<MethodInfo> DexParser::get_methods() const {
141
+ std::vector<MethodInfo> methods;
142
+
143
+ if (header_.method_ids_off == 0 || header_.method_ids_size == 0) {
144
+ return methods;
145
+ }
146
+
147
+ for (uint32_t i = 0; i < header_.method_ids_size; i++) {
148
+ size_t offset = header_.method_ids_off + i * 8;
149
+ if (offset + 8 > data_.size()) break;
150
+
151
+ uint16_t class_idx = read_le<uint16_t>(&data_[offset]);
152
+ uint16_t proto_idx = read_le<uint16_t>(&data_[offset + 2]);
153
+ uint32_t name_idx = read_le<uint32_t>(&data_[offset + 4]);
154
+
155
+ MethodInfo info;
156
+ info.class_name = get_class_name(class_idx);
157
+ if (name_idx < strings_.size()) {
158
+ info.method_name = strings_[name_idx];
159
+ }
160
+ info.prototype = get_proto_string(proto_idx);
161
+ info.access_flags = 0;
162
+ info.code_off = 0;
163
+
164
+ methods.push_back(info);
165
+ }
166
+
167
+ return methods;
168
+ }
169
+
170
+ std::vector<FieldInfo> DexParser::get_fields() const {
171
+ std::vector<FieldInfo> fields;
172
+
173
+ if (header_.field_ids_off == 0 || header_.field_ids_size == 0) {
174
+ return fields;
175
+ }
176
+
177
+ for (uint32_t i = 0; i < header_.field_ids_size; i++) {
178
+ size_t offset = header_.field_ids_off + i * 8;
179
+ if (offset + 8 > data_.size()) break;
180
+
181
+ uint16_t class_idx = read_le<uint16_t>(&data_[offset]);
182
+ uint16_t type_idx = read_le<uint16_t>(&data_[offset + 2]);
183
+ uint32_t name_idx = read_le<uint32_t>(&data_[offset + 4]);
184
+
185
+ FieldInfo info;
186
+ info.class_name = get_class_name(class_idx);
187
+ info.type_name = get_class_name(type_idx);
188
+ if (name_idx < strings_.size()) {
189
+ info.field_name = strings_[name_idx];
190
+ }
191
+ info.access_flags = 0;
192
+
193
+ fields.push_back(info);
194
+ }
195
+
196
+ return fields;
197
+ }
198
+
199
+ std::vector<std::string> DexParser::get_class_methods(const std::string& class_name) const {
200
+ std::vector<std::string> result;
201
+ auto methods = get_methods();
202
+
203
+ for (const auto& m : methods) {
204
+ if (m.class_name == class_name) {
205
+ result.push_back(m.method_name);
206
+ }
207
+ }
208
+
209
+ return result;
210
+ }
211
+
212
+ std::string DexParser::get_info() const {
213
+ std::stringstream ss;
214
+
215
+ ss << "DEX File Info:\n";
216
+ ss << " Version: " << std::string(reinterpret_cast<const char*>(header_.magic + 4), 3) << "\n";
217
+ ss << " File Size: " << header_.file_size << " bytes\n";
218
+ ss << " Strings: " << header_.string_ids_size << "\n";
219
+ ss << " Types: " << header_.type_ids_size << "\n";
220
+ ss << " Protos: " << header_.proto_ids_size << "\n";
221
+ ss << " Fields: " << header_.field_ids_size << "\n";
222
+ ss << " Methods: " << header_.method_ids_size << "\n";
223
+ ss << " Classes: " << header_.class_defs_size << "\n";
224
+
225
+ return ss.str();
226
+ }
227
+
228
+ bool DexParser::get_method_code(const std::string& class_name, const std::string& method_name, CodeItem& code) const {
229
+ // Find the class
230
+ for (const auto& cls : classes_) {
231
+ std::string cls_name = get_class_name(cls.class_idx);
232
+ if (cls_name != class_name) continue;
233
+
234
+ if (cls.class_data_off == 0) continue;
235
+
236
+ // Parse class_data_item
237
+ size_t offset = cls.class_data_off;
238
+ uint32_t static_fields_size = read_uleb128(offset);
239
+ uint32_t instance_fields_size = read_uleb128(offset);
240
+ uint32_t direct_methods_size = read_uleb128(offset);
241
+ uint32_t virtual_methods_size = read_uleb128(offset);
242
+
243
+ // Skip fields
244
+ for (uint32_t i = 0; i < static_fields_size + instance_fields_size; i++) {
245
+ read_uleb128(offset); // field_idx_diff
246
+ read_uleb128(offset); // access_flags
247
+ }
248
+
249
+ // Parse methods
250
+ uint32_t method_idx = 0;
251
+ for (uint32_t i = 0; i < direct_methods_size + virtual_methods_size; i++) {
252
+ uint32_t method_idx_diff = read_uleb128(offset);
253
+ method_idx += method_idx_diff;
254
+ uint32_t access_flags = read_uleb128(offset);
255
+ uint32_t code_off = read_uleb128(offset);
256
+
257
+ // Get method name from method_ids
258
+ if (method_idx < header_.method_ids_size) {
259
+ size_t mid_off = header_.method_ids_off + method_idx * 8;
260
+ if (mid_off + 8 <= data_.size()) {
261
+ uint32_t name_idx = read_le<uint32_t>(&data_[mid_off + 4]);
262
+ if (name_idx < strings_.size() && strings_[name_idx] == method_name) {
263
+ // Found the method
264
+ if (code_off == 0) return false; // No code (abstract/native)
265
+
266
+ // Parse code_item
267
+ if (code_off + 16 > data_.size()) return false;
268
+
269
+ code.registers_size = read_le<uint16_t>(&data_[code_off]);
270
+ code.ins_size = read_le<uint16_t>(&data_[code_off + 2]);
271
+ code.outs_size = read_le<uint16_t>(&data_[code_off + 4]);
272
+ code.tries_size = read_le<uint16_t>(&data_[code_off + 6]);
273
+ code.debug_info_off = read_le<uint32_t>(&data_[code_off + 8]);
274
+ code.insns_size = read_le<uint32_t>(&data_[code_off + 12]);
275
+
276
+ size_t insns_off = code_off + 16;
277
+ size_t insns_bytes = code.insns_size * 2;
278
+
279
+ if (insns_off + insns_bytes > data_.size()) return false;
280
+
281
+ code.insns.assign(data_.begin() + insns_off,
282
+ data_.begin() + insns_off + insns_bytes);
283
+ code.code_off = code_off;
284
+ return true;
285
+ }
286
+ }
287
+ }
288
+ }
289
+ }
290
+ return false;
291
+ }
292
+
293
+ std::unordered_map<std::string, CodeItem> DexParser::get_all_method_codes() const {
294
+ std::unordered_map<std::string, CodeItem> result;
295
+
296
+ for (const auto& cls : classes_) {
297
+ std::string cls_name = get_class_name(cls.class_idx);
298
+ if (cls_name.empty() || cls.class_data_off == 0) continue;
299
+
300
+ // Parse class_data_item
301
+ size_t offset = cls.class_data_off;
302
+ uint32_t static_fields_size = read_uleb128(offset);
303
+ uint32_t instance_fields_size = read_uleb128(offset);
304
+ uint32_t direct_methods_size = read_uleb128(offset);
305
+ uint32_t virtual_methods_size = read_uleb128(offset);
306
+
307
+ // Skip fields
308
+ for (uint32_t i = 0; i < static_fields_size + instance_fields_size; i++) {
309
+ read_uleb128(offset);
310
+ read_uleb128(offset);
311
+ }
312
+
313
+ // Parse all methods
314
+ uint32_t method_idx = 0;
315
+ for (uint32_t i = 0; i < direct_methods_size + virtual_methods_size; i++) {
316
+ uint32_t method_idx_diff = read_uleb128(offset);
317
+ method_idx += method_idx_diff;
318
+ read_uleb128(offset); // access_flags
319
+ uint32_t code_off = read_uleb128(offset);
320
+
321
+ if (code_off == 0) continue; // No code
322
+ if (method_idx >= header_.method_ids_size) continue;
323
+
324
+ size_t mid_off = header_.method_ids_off + method_idx * 8;
325
+ if (mid_off + 8 > data_.size()) continue;
326
+
327
+ uint32_t name_idx = read_le<uint32_t>(&data_[mid_off + 4]);
328
+ if (name_idx >= strings_.size()) continue;
329
+
330
+ std::string method_name = strings_[name_idx];
331
+ std::string key = cls_name + "|" + method_name;
332
+
333
+ // Parse code_item
334
+ if (code_off + 16 > data_.size()) continue;
335
+
336
+ CodeItem code;
337
+ code.registers_size = read_le<uint16_t>(&data_[code_off]);
338
+ code.ins_size = read_le<uint16_t>(&data_[code_off + 2]);
339
+ code.outs_size = read_le<uint16_t>(&data_[code_off + 4]);
340
+ code.tries_size = read_le<uint16_t>(&data_[code_off + 6]);
341
+ code.debug_info_off = read_le<uint32_t>(&data_[code_off + 8]);
342
+ code.insns_size = read_le<uint32_t>(&data_[code_off + 12]);
343
+ code.code_off = code_off;
344
+
345
+ size_t insns_off = code_off + 16;
346
+ size_t insns_bytes = code.insns_size * 2;
347
+
348
+ if (insns_off + insns_bytes > data_.size()) continue;
349
+
350
+ code.insns.assign(data_.begin() + insns_off,
351
+ data_.begin() + insns_off + insns_bytes);
352
+
353
+ result[key] = std::move(code);
354
+ }
355
+ }
356
+
357
+ return result;
358
+ }
359
+
360
+ std::string DexParser::get_proto_string(uint32_t proto_idx) const {
361
+ // proto_id: shorty_idx(4), return_type_idx(4), parameters_off(4)
362
+ size_t offset = header_.proto_ids_off + proto_idx * 12;
363
+ if (offset + 12 > data_.size()) return "()V";
364
+
365
+ uint32_t return_type_idx = read_le<uint32_t>(&data_[offset + 4]);
366
+ uint32_t params_off = read_le<uint32_t>(&data_[offset + 8]);
367
+
368
+ std::string result = "(";
369
+
370
+ // Parse parameters if present
371
+ if (params_off != 0 && params_off + 4 <= data_.size()) {
372
+ uint32_t param_count = read_le<uint32_t>(&data_[params_off]);
373
+ for (uint32_t i = 0; i < param_count && params_off + 4 + (i + 1) * 2 <= data_.size(); i++) {
374
+ uint16_t type_idx = read_le<uint16_t>(&data_[params_off + 4 + i * 2]);
375
+ result += get_class_name(type_idx);
376
+ }
377
+ }
378
+
379
+ result += ")";
380
+ result += get_class_name(return_type_idx);
381
+
382
+ return result;
383
+ }
384
+
385
+ std::string DexParser::get_full_method_signature(uint32_t method_idx) const {
386
+ if (method_idx >= header_.method_ids_size) return "";
387
+
388
+ size_t offset = header_.method_ids_off + method_idx * 8;
389
+ if (offset + 8 > data_.size()) return "";
390
+
391
+ uint16_t class_idx = read_le<uint16_t>(&data_[offset]);
392
+ uint16_t proto_idx = read_le<uint16_t>(&data_[offset + 2]);
393
+ uint32_t name_idx = read_le<uint32_t>(&data_[offset + 4]);
394
+
395
+ std::string sig = get_class_name(class_idx) + "->";
396
+ if (name_idx < strings_.size()) {
397
+ sig += strings_[name_idx];
398
+ }
399
+ sig += get_proto_string(proto_idx);
400
+
401
+ return sig;
402
+ }
403
+
404
+ std::vector<std::string> DexParser::get_method_signatures() const {
405
+ std::vector<std::string> sigs;
406
+
407
+ for (uint32_t i = 0; i < header_.method_ids_size; i++) {
408
+ sigs.push_back(get_full_method_signature(i));
409
+ }
410
+
411
+ return sigs;
412
+ }
413
+
414
+ std::vector<std::string> DexParser::get_field_signatures() const {
415
+ std::vector<std::string> sigs;
416
+
417
+ for (uint32_t i = 0; i < header_.field_ids_size; i++) {
418
+ size_t offset = header_.field_ids_off + i * 8;
419
+ if (offset + 8 > data_.size()) break;
420
+
421
+ uint16_t class_idx = read_le<uint16_t>(&data_[offset]);
422
+ uint16_t type_idx = read_le<uint16_t>(&data_[offset + 2]);
423
+ uint32_t name_idx = read_le<uint32_t>(&data_[offset + 4]);
424
+
425
+ std::string sig = get_class_name(class_idx) + "->";
426
+ if (name_idx < strings_.size()) {
427
+ sig += strings_[name_idx];
428
+ }
429
+ sig += ":" + get_class_name(type_idx);
430
+ sigs.push_back(sig);
431
+ }
432
+
433
+ return sigs;
434
+ }
435
+
436
+ std::vector<DexParser::XRef> DexParser::find_method_xrefs(const std::string& class_name, const std::string& method_name) const {
437
+ std::vector<XRef> results;
438
+
439
+ // Find the target method index
440
+ int target_method_idx = -1;
441
+ for (uint32_t i = 0; i < header_.method_ids_size; i++) {
442
+ size_t offset = header_.method_ids_off + i * 8;
443
+ if (offset + 8 > data_.size()) break;
444
+
445
+ uint16_t cls_idx = read_le<uint16_t>(&data_[offset]);
446
+ uint32_t name_idx = read_le<uint32_t>(&data_[offset + 4]);
447
+
448
+ if (get_class_name(cls_idx) == class_name &&
449
+ name_idx < strings_.size() && strings_[name_idx] == method_name) {
450
+ target_method_idx = i;
451
+ break;
452
+ }
453
+ }
454
+
455
+ if (target_method_idx < 0) return results;
456
+
457
+ // Scan all methods for invoke instructions
458
+ for (const auto& cls : classes_) {
459
+ std::string caller_class = get_class_name(cls.class_idx);
460
+ if (caller_class.empty() || cls.class_data_off == 0) continue;
461
+
462
+ size_t offset = cls.class_data_off;
463
+ uint32_t static_fields = read_uleb128(offset);
464
+ uint32_t instance_fields = read_uleb128(offset);
465
+ uint32_t direct_methods = read_uleb128(offset);
466
+ uint32_t virtual_methods = read_uleb128(offset);
467
+
468
+ // Skip fields
469
+ for (uint32_t i = 0; i < static_fields + instance_fields; i++) {
470
+ read_uleb128(offset);
471
+ read_uleb128(offset);
472
+ }
473
+
474
+ // Scan methods
475
+ uint32_t method_idx = 0;
476
+ for (uint32_t i = 0; i < direct_methods + virtual_methods; i++) {
477
+ method_idx += read_uleb128(offset);
478
+ read_uleb128(offset); // access_flags
479
+ uint32_t code_off = read_uleb128(offset);
480
+
481
+ if (code_off == 0) continue;
482
+ if (code_off + 16 > data_.size()) continue;
483
+
484
+ uint32_t insns_size = read_le<uint32_t>(&data_[code_off + 12]);
485
+ size_t insns_off = code_off + 16;
486
+
487
+ // Get caller method name
488
+ std::string caller_method;
489
+ if (method_idx < header_.method_ids_size) {
490
+ size_t mid_off = header_.method_ids_off + method_idx * 8;
491
+ if (mid_off + 8 <= data_.size()) {
492
+ uint32_t name_idx = read_le<uint32_t>(&data_[mid_off + 4]);
493
+ if (name_idx < strings_.size()) {
494
+ caller_method = strings_[name_idx];
495
+ }
496
+ }
497
+ }
498
+
499
+ // Scan bytecode for invoke instructions
500
+ for (size_t pos = 0; pos < insns_size * 2;) {
501
+ if (insns_off + pos >= data_.size()) break;
502
+
503
+ uint8_t opcode = data_[insns_off + pos];
504
+
505
+ // invoke-* opcodes: 0x6e-0x72 (invoke-virtual to invoke-interface)
506
+ // invoke-*/range: 0x74-0x78
507
+ if ((opcode >= 0x6e && opcode <= 0x72) || (opcode >= 0x74 && opcode <= 0x78)) {
508
+ if (insns_off + pos + 3 <= data_.size()) {
509
+ uint16_t ref_idx = read_le<uint16_t>(&data_[insns_off + pos + 2]);
510
+ if (ref_idx == target_method_idx) {
511
+ XRef xref;
512
+ xref.caller_class = caller_class;
513
+ xref.caller_method = caller_method;
514
+ xref.offset = static_cast<uint32_t>(pos / 2);
515
+ results.push_back(xref);
516
+ }
517
+ }
518
+ pos += 6; // invoke instructions are 3 units (6 bytes)
519
+ } else {
520
+ // Simple instruction size estimation
521
+ pos += 2;
522
+ if (opcode == 0x00 && pos > 2) break; // nop might indicate padding
523
+ }
524
+ }
525
+ }
526
+ }
527
+
528
+ return results;
529
+ }
530
+
531
+ std::vector<DexParser::XRef> DexParser::find_field_xrefs(const std::string& class_name, const std::string& field_name) const {
532
+ std::vector<XRef> results;
533
+
534
+ // Find the target field index
535
+ int target_field_idx = -1;
536
+ for (uint32_t i = 0; i < header_.field_ids_size; i++) {
537
+ size_t offset = header_.field_ids_off + i * 8;
538
+ if (offset + 8 > data_.size()) break;
539
+
540
+ uint16_t cls_idx = read_le<uint16_t>(&data_[offset]);
541
+ uint32_t name_idx = read_le<uint32_t>(&data_[offset + 4]);
542
+
543
+ if (get_class_name(cls_idx) == class_name &&
544
+ name_idx < strings_.size() && strings_[name_idx] == field_name) {
545
+ target_field_idx = i;
546
+ break;
547
+ }
548
+ }
549
+
550
+ if (target_field_idx < 0) return results;
551
+
552
+ // Scan all methods for field access instructions
553
+ for (const auto& cls : classes_) {
554
+ std::string caller_class = get_class_name(cls.class_idx);
555
+ if (caller_class.empty() || cls.class_data_off == 0) continue;
556
+
557
+ size_t offset = cls.class_data_off;
558
+ uint32_t static_fields = read_uleb128(offset);
559
+ uint32_t instance_fields = read_uleb128(offset);
560
+ uint32_t direct_methods = read_uleb128(offset);
561
+ uint32_t virtual_methods = read_uleb128(offset);
562
+
563
+ for (uint32_t i = 0; i < static_fields + instance_fields; i++) {
564
+ read_uleb128(offset);
565
+ read_uleb128(offset);
566
+ }
567
+
568
+ uint32_t method_idx = 0;
569
+ for (uint32_t i = 0; i < direct_methods + virtual_methods; i++) {
570
+ method_idx += read_uleb128(offset);
571
+ read_uleb128(offset);
572
+ uint32_t code_off = read_uleb128(offset);
573
+
574
+ if (code_off == 0) continue;
575
+ if (code_off + 16 > data_.size()) continue;
576
+
577
+ uint32_t insns_size = read_le<uint32_t>(&data_[code_off + 12]);
578
+ size_t insns_off = code_off + 16;
579
+
580
+ std::string caller_method;
581
+ if (method_idx < header_.method_ids_size) {
582
+ size_t mid_off = header_.method_ids_off + method_idx * 8;
583
+ if (mid_off + 8 <= data_.size()) {
584
+ uint32_t name_idx = read_le<uint32_t>(&data_[mid_off + 4]);
585
+ if (name_idx < strings_.size()) {
586
+ caller_method = strings_[name_idx];
587
+ }
588
+ }
589
+ }
590
+
591
+ for (size_t pos = 0; pos < insns_size * 2;) {
592
+ if (insns_off + pos >= data_.size()) break;
593
+
594
+ uint8_t opcode = data_[insns_off + pos];
595
+
596
+ // iget/iput: 0x52-0x5f, sget/sput: 0x60-0x6d
597
+ if ((opcode >= 0x52 && opcode <= 0x5f) || (opcode >= 0x60 && opcode <= 0x6d)) {
598
+ if (insns_off + pos + 3 <= data_.size()) {
599
+ uint16_t ref_idx = read_le<uint16_t>(&data_[insns_off + pos + 2]);
600
+ if (ref_idx == target_field_idx) {
601
+ XRef xref;
602
+ xref.caller_class = caller_class;
603
+ xref.caller_method = caller_method;
604
+ xref.offset = static_cast<uint32_t>(pos / 2);
605
+ results.push_back(xref);
606
+ }
607
+ }
608
+ pos += 4; // field instructions are 2 units (4 bytes)
609
+ } else {
610
+ pos += 2;
611
+ if (opcode == 0x00 && pos > 2) break;
612
+ }
613
+ }
614
+ }
615
+ }
616
+
617
+ return results;
618
+ }
619
+
620
+ } // namespace dex