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.
- 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 +146 -50
- package/package.json +1 -1
- package/android/src/main/java/com/aetherlink/dexeditor/RustDex.java +0 -203
- 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,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
|