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,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
|