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,576 @@
|
|
|
1
|
+
#include "dex/smali_to_java.h"
|
|
2
|
+
#include <regex>
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
|
|
5
|
+
namespace dex {
|
|
6
|
+
|
|
7
|
+
std::vector<std::string> SmaliToJava::split(const std::string& s, char delim) {
|
|
8
|
+
std::vector<std::string> result;
|
|
9
|
+
std::stringstream ss(s);
|
|
10
|
+
std::string item;
|
|
11
|
+
while (std::getline(ss, item, delim)) {
|
|
12
|
+
if (!item.empty()) result.push_back(item);
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
std::string SmaliToJava::trim(const std::string& s) {
|
|
18
|
+
size_t start = s.find_first_not_of(" \t\r\n");
|
|
19
|
+
if (start == std::string::npos) return "";
|
|
20
|
+
size_t end = s.find_last_not_of(" \t\r\n");
|
|
21
|
+
return s.substr(start, end - start + 1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
std::string SmaliToJava::get_indent() {
|
|
25
|
+
return std::string(indent_ * 4, ' ');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
std::string SmaliToJava::type_to_java(const std::string& smali_type) {
|
|
29
|
+
if (smali_type.empty()) return "void";
|
|
30
|
+
|
|
31
|
+
std::string t = smali_type;
|
|
32
|
+
int array_dim = 0;
|
|
33
|
+
while (!t.empty() && t[0] == '[') {
|
|
34
|
+
array_dim++;
|
|
35
|
+
t = t.substr(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
std::string base;
|
|
39
|
+
if (t == "V") base = "void";
|
|
40
|
+
else if (t == "Z") base = "boolean";
|
|
41
|
+
else if (t == "B") base = "byte";
|
|
42
|
+
else if (t == "S") base = "short";
|
|
43
|
+
else if (t == "C") base = "char";
|
|
44
|
+
else if (t == "I") base = "int";
|
|
45
|
+
else if (t == "J") base = "long";
|
|
46
|
+
else if (t == "F") base = "float";
|
|
47
|
+
else if (t == "D") base = "double";
|
|
48
|
+
else if (t[0] == 'L' && t.back() == ';') {
|
|
49
|
+
base = t.substr(1, t.length() - 2);
|
|
50
|
+
std::replace(base.begin(), base.end(), '/', '.');
|
|
51
|
+
} else {
|
|
52
|
+
base = t;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (int i = 0; i < array_dim; i++) base += "[]";
|
|
56
|
+
return base;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
std::string SmaliToJava::method_to_java(const std::string& method_ref) {
|
|
60
|
+
// Lcom/example/Class;->methodName(params)ReturnType
|
|
61
|
+
size_t arrow = method_ref.find("->");
|
|
62
|
+
if (arrow == std::string::npos) return method_ref;
|
|
63
|
+
|
|
64
|
+
std::string class_part = method_ref.substr(0, arrow);
|
|
65
|
+
std::string method_part = method_ref.substr(arrow + 2);
|
|
66
|
+
|
|
67
|
+
size_t paren = method_part.find('(');
|
|
68
|
+
std::string method_name = (paren != std::string::npos) ? method_part.substr(0, paren) : method_part;
|
|
69
|
+
|
|
70
|
+
std::string class_name = type_to_java(class_part);
|
|
71
|
+
size_t last_dot = class_name.rfind('.');
|
|
72
|
+
if (last_dot != std::string::npos) {
|
|
73
|
+
class_name = class_name.substr(last_dot + 1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return class_name + "." + method_name;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
std::string SmaliToJava::convert_const(const std::string& line) {
|
|
80
|
+
// const-string v0, "hello"
|
|
81
|
+
// const/4 v0, 0x1
|
|
82
|
+
// const/16 v0, 0x100
|
|
83
|
+
std::regex const_string_re(R"(const-string\s+(\w+),\s*\"(.*)\")");
|
|
84
|
+
std::regex const_num_re(R"(const(?:/\d+|/high\d+)?\s+(\w+),\s*(-?0x[0-9a-fA-F]+|-?\d+))");
|
|
85
|
+
std::regex const_class_re(R"(const-class\s+(\w+),\s*(\S+))");
|
|
86
|
+
std::smatch match;
|
|
87
|
+
|
|
88
|
+
if (std::regex_search(line, match, const_string_re)) {
|
|
89
|
+
std::string reg = match[1].str();
|
|
90
|
+
std::string value = match[2].str();
|
|
91
|
+
registers_[reg] = {"String", "\"" + value + "\""};
|
|
92
|
+
return get_indent() + "String " + reg + " = \"" + value + "\";";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (std::regex_search(line, match, const_class_re)) {
|
|
96
|
+
std::string reg = match[1].str();
|
|
97
|
+
std::string cls = type_to_java(match[2].str());
|
|
98
|
+
registers_[reg] = {"Class", cls + ".class"};
|
|
99
|
+
return get_indent() + "Class " + reg + " = " + cls + ".class;";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (std::regex_search(line, match, const_num_re)) {
|
|
103
|
+
std::string reg = match[1].str();
|
|
104
|
+
std::string value = match[2].str();
|
|
105
|
+
registers_[reg] = {"int", value};
|
|
106
|
+
return get_indent() + "int " + reg + " = " + value + ";";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return "";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
std::string SmaliToJava::convert_move(const std::string& line) {
|
|
113
|
+
// move v0, v1
|
|
114
|
+
// move-result v0
|
|
115
|
+
// move-object v0, v1
|
|
116
|
+
std::regex move_re(R"(move(?:-object|-wide|-result(?:-object|-wide)?)?\s+(\w+)(?:,\s*(\w+))?)");
|
|
117
|
+
std::smatch match;
|
|
118
|
+
|
|
119
|
+
if (std::regex_search(line, match, move_re)) {
|
|
120
|
+
std::string dst = match[1].str();
|
|
121
|
+
if (match[2].matched) {
|
|
122
|
+
std::string src = match[2].str();
|
|
123
|
+
std::string type = registers_.count(src) ? registers_[src].type : "Object";
|
|
124
|
+
registers_[dst] = {type, src};
|
|
125
|
+
return get_indent() + dst + " = " + src + ";";
|
|
126
|
+
} else {
|
|
127
|
+
// move-result
|
|
128
|
+
registers_[dst] = {"Object", "result"};
|
|
129
|
+
return get_indent() + "// " + dst + " = <result>";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return "";
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
std::string SmaliToJava::convert_invoke(const std::string& line) {
|
|
136
|
+
// invoke-virtual {v0, v1}, Lcom/example/Class;->method(I)V
|
|
137
|
+
std::regex invoke_re(R"(invoke-(\w+)(?:/range)?\s*\{([^}]*)\},\s*(\S+))");
|
|
138
|
+
std::smatch match;
|
|
139
|
+
|
|
140
|
+
if (std::regex_search(line, match, invoke_re)) {
|
|
141
|
+
std::string invoke_type = match[1].str();
|
|
142
|
+
std::string regs_str = match[2].str();
|
|
143
|
+
std::string method_ref = match[3].str();
|
|
144
|
+
|
|
145
|
+
// Parse registers
|
|
146
|
+
std::vector<std::string> regs;
|
|
147
|
+
std::regex reg_re(R"(\w+)");
|
|
148
|
+
std::sregex_iterator it(regs_str.begin(), regs_str.end(), reg_re);
|
|
149
|
+
std::sregex_iterator end;
|
|
150
|
+
while (it != end) {
|
|
151
|
+
regs.push_back(it->str());
|
|
152
|
+
++it;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Parse method reference
|
|
156
|
+
size_t arrow = method_ref.find("->");
|
|
157
|
+
std::string class_name = (arrow != std::string::npos) ?
|
|
158
|
+
type_to_java(method_ref.substr(0, arrow)) : "";
|
|
159
|
+
|
|
160
|
+
std::string method_part = (arrow != std::string::npos) ?
|
|
161
|
+
method_ref.substr(arrow + 2) : method_ref;
|
|
162
|
+
size_t paren = method_part.find('(');
|
|
163
|
+
std::string method_name = (paren != std::string::npos) ?
|
|
164
|
+
method_part.substr(0, paren) : method_part;
|
|
165
|
+
|
|
166
|
+
// Build call
|
|
167
|
+
std::string result;
|
|
168
|
+
if (invoke_type == "static") {
|
|
169
|
+
result = class_name + "." + method_name + "(";
|
|
170
|
+
for (size_t i = 0; i < regs.size(); i++) {
|
|
171
|
+
if (i > 0) result += ", ";
|
|
172
|
+
result += regs[i];
|
|
173
|
+
}
|
|
174
|
+
result += ")";
|
|
175
|
+
} else {
|
|
176
|
+
// instance method: first reg is 'this'
|
|
177
|
+
std::string obj = regs.empty() ? "this" : regs[0];
|
|
178
|
+
if (method_name == "<init>") {
|
|
179
|
+
// Constructor
|
|
180
|
+
result = "new " + class_name + "(";
|
|
181
|
+
} else {
|
|
182
|
+
result = obj + "." + method_name + "(";
|
|
183
|
+
}
|
|
184
|
+
for (size_t i = 1; i < regs.size(); i++) {
|
|
185
|
+
if (i > 1) result += ", ";
|
|
186
|
+
result += regs[i];
|
|
187
|
+
}
|
|
188
|
+
result += ")";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return get_indent() + result + ";";
|
|
192
|
+
}
|
|
193
|
+
return "";
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
std::string SmaliToJava::convert_field_access(const std::string& line) {
|
|
197
|
+
// iget v0, p0, Lcom/example/Class;->field:I
|
|
198
|
+
// sget v0, Lcom/example/Class;->field:I
|
|
199
|
+
// iput v0, p0, Lcom/example/Class;->field:I
|
|
200
|
+
std::regex field_re(R"((i|s)(get|put)(?:-\w+)?\s+(\w+),\s*(?:(\w+),\s*)?(\S+))");
|
|
201
|
+
std::smatch match;
|
|
202
|
+
|
|
203
|
+
if (std::regex_search(line, match, field_re)) {
|
|
204
|
+
std::string is_static = match[1].str();
|
|
205
|
+
std::string is_get = match[2].str();
|
|
206
|
+
std::string val_reg = match[3].str();
|
|
207
|
+
std::string obj_reg = match[4].matched ? match[4].str() : "";
|
|
208
|
+
std::string field_ref = match[5].str();
|
|
209
|
+
|
|
210
|
+
// Parse field: Lcom/example/Class;->fieldName:Type
|
|
211
|
+
size_t arrow = field_ref.find("->");
|
|
212
|
+
size_t colon = field_ref.rfind(':');
|
|
213
|
+
std::string class_name = (arrow != std::string::npos) ?
|
|
214
|
+
type_to_java(field_ref.substr(0, arrow)) : "";
|
|
215
|
+
std::string field_name = (arrow != std::string::npos && colon != std::string::npos) ?
|
|
216
|
+
field_ref.substr(arrow + 2, colon - arrow - 2) : field_ref;
|
|
217
|
+
std::string field_type = (colon != std::string::npos) ?
|
|
218
|
+
type_to_java(field_ref.substr(colon + 1)) : "Object";
|
|
219
|
+
|
|
220
|
+
if (is_get == "get") {
|
|
221
|
+
std::string src = (is_static == "s") ?
|
|
222
|
+
class_name + "." + field_name :
|
|
223
|
+
obj_reg + "." + field_name;
|
|
224
|
+
registers_[val_reg] = {field_type, src};
|
|
225
|
+
return get_indent() + field_type + " " + val_reg + " = " + src + ";";
|
|
226
|
+
} else {
|
|
227
|
+
std::string dst = (is_static == "s") ?
|
|
228
|
+
class_name + "." + field_name :
|
|
229
|
+
obj_reg + "." + field_name;
|
|
230
|
+
return get_indent() + dst + " = " + val_reg + ";";
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return "";
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
std::string SmaliToJava::convert_return(const std::string& line) {
|
|
237
|
+
std::regex return_re(R"(return(?:-void|-object|-wide)?\s*(\w+)?)");
|
|
238
|
+
std::smatch match;
|
|
239
|
+
|
|
240
|
+
if (std::regex_search(line, match, return_re)) {
|
|
241
|
+
if (match[1].matched && !match[1].str().empty()) {
|
|
242
|
+
return get_indent() + "return " + match[1].str() + ";";
|
|
243
|
+
} else {
|
|
244
|
+
return get_indent() + "return;";
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return "";
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
std::string SmaliToJava::convert_if(const std::string& line) {
|
|
251
|
+
// if-eqz v0, :label
|
|
252
|
+
// if-eq v0, v1, :label
|
|
253
|
+
std::regex if_z_re(R"(if-(eq|ne|lt|ge|gt|le)z\s+(\w+),\s*:(\w+))");
|
|
254
|
+
std::regex if_re(R"(if-(eq|ne|lt|ge|gt|le)\s+(\w+),\s*(\w+),\s*:(\w+))");
|
|
255
|
+
std::smatch match;
|
|
256
|
+
|
|
257
|
+
std::string op_map_z, op_map;
|
|
258
|
+
|
|
259
|
+
if (std::regex_search(line, match, if_z_re)) {
|
|
260
|
+
std::string op = match[1].str();
|
|
261
|
+
std::string reg = match[2].str();
|
|
262
|
+
std::string label = match[3].str();
|
|
263
|
+
|
|
264
|
+
std::string java_op;
|
|
265
|
+
if (op == "eq") java_op = "==";
|
|
266
|
+
else if (op == "ne") java_op = "!=";
|
|
267
|
+
else if (op == "lt") java_op = "<";
|
|
268
|
+
else if (op == "ge") java_op = ">=";
|
|
269
|
+
else if (op == "gt") java_op = ">";
|
|
270
|
+
else if (op == "le") java_op = "<=";
|
|
271
|
+
|
|
272
|
+
return get_indent() + "if (" + reg + " " + java_op + " 0) goto " + label + ";";
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (std::regex_search(line, match, if_re)) {
|
|
276
|
+
std::string op = match[1].str();
|
|
277
|
+
std::string reg1 = match[2].str();
|
|
278
|
+
std::string reg2 = match[3].str();
|
|
279
|
+
std::string label = match[4].str();
|
|
280
|
+
|
|
281
|
+
std::string java_op;
|
|
282
|
+
if (op == "eq") java_op = "==";
|
|
283
|
+
else if (op == "ne") java_op = "!=";
|
|
284
|
+
else if (op == "lt") java_op = "<";
|
|
285
|
+
else if (op == "ge") java_op = ">=";
|
|
286
|
+
else if (op == "gt") java_op = ">";
|
|
287
|
+
else if (op == "le") java_op = "<=";
|
|
288
|
+
|
|
289
|
+
return get_indent() + "if (" + reg1 + " " + java_op + " " + reg2 + ") goto " + label + ";";
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return "";
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
std::string SmaliToJava::convert_new(const std::string& line) {
|
|
296
|
+
// new-instance v0, Lcom/example/Class;
|
|
297
|
+
// new-array v0, v1, [I
|
|
298
|
+
std::regex new_instance_re(R"(new-instance\s+(\w+),\s*(\S+))");
|
|
299
|
+
std::regex new_array_re(R"(new-array\s+(\w+),\s*(\w+),\s*(\S+))");
|
|
300
|
+
std::smatch match;
|
|
301
|
+
|
|
302
|
+
if (std::regex_search(line, match, new_instance_re)) {
|
|
303
|
+
std::string reg = match[1].str();
|
|
304
|
+
std::string type = type_to_java(match[2].str());
|
|
305
|
+
registers_[reg] = {type, "new " + type + "()"};
|
|
306
|
+
return get_indent() + type + " " + reg + " = new " + type + "();";
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (std::regex_search(line, match, new_array_re)) {
|
|
310
|
+
std::string reg = match[1].str();
|
|
311
|
+
std::string size_reg = match[2].str();
|
|
312
|
+
std::string type = type_to_java(match[3].str());
|
|
313
|
+
registers_[reg] = {type, "new " + type + "[" + size_reg + "]"};
|
|
314
|
+
return get_indent() + type + " " + reg + " = new " + type.substr(0, type.length()-2) + "[" + size_reg + "];";
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return "";
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
std::string SmaliToJava::convert_arithmetic(const std::string& line) {
|
|
321
|
+
// add-int v0, v1, v2
|
|
322
|
+
// mul-int/lit8 v0, v1, 0x10
|
|
323
|
+
std::regex arith_re(R"((add|sub|mul|div|rem|and|or|xor|shl|shr|ushr)-(\w+)(?:/\w+)?\s+(\w+),\s*(\w+),\s*(\S+))");
|
|
324
|
+
std::regex arith2_re(R"((add|sub|mul|div|rem|and|or|xor|shl|shr|ushr)-(\w+)(?:/2addr)?\s+(\w+),\s*(\w+))");
|
|
325
|
+
std::regex neg_re(R"((neg|not)-(\w+)\s+(\w+),\s*(\w+))");
|
|
326
|
+
std::smatch match;
|
|
327
|
+
|
|
328
|
+
if (std::regex_search(line, match, arith_re)) {
|
|
329
|
+
std::string op = match[1].str();
|
|
330
|
+
std::string dst = match[3].str();
|
|
331
|
+
std::string src1 = match[4].str();
|
|
332
|
+
std::string src2 = match[5].str();
|
|
333
|
+
|
|
334
|
+
std::string java_op;
|
|
335
|
+
if (op == "add") java_op = "+";
|
|
336
|
+
else if (op == "sub") java_op = "-";
|
|
337
|
+
else if (op == "mul") java_op = "*";
|
|
338
|
+
else if (op == "div") java_op = "/";
|
|
339
|
+
else if (op == "rem") java_op = "%";
|
|
340
|
+
else if (op == "and") java_op = "&";
|
|
341
|
+
else if (op == "or") java_op = "|";
|
|
342
|
+
else if (op == "xor") java_op = "^";
|
|
343
|
+
else if (op == "shl") java_op = "<<";
|
|
344
|
+
else if (op == "shr") java_op = ">>";
|
|
345
|
+
else if (op == "ushr") java_op = ">>>";
|
|
346
|
+
|
|
347
|
+
return get_indent() + dst + " = " + src1 + " " + java_op + " " + src2 + ";";
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (std::regex_search(line, match, arith2_re)) {
|
|
351
|
+
std::string op = match[1].str();
|
|
352
|
+
std::string dst = match[3].str();
|
|
353
|
+
std::string src = match[4].str();
|
|
354
|
+
|
|
355
|
+
std::string java_op;
|
|
356
|
+
if (op == "add") java_op = "+=";
|
|
357
|
+
else if (op == "sub") java_op = "-=";
|
|
358
|
+
else if (op == "mul") java_op = "*=";
|
|
359
|
+
else if (op == "div") java_op = "/=";
|
|
360
|
+
else java_op = op + "=";
|
|
361
|
+
|
|
362
|
+
return get_indent() + dst + " " + java_op + " " + src + ";";
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (std::regex_search(line, match, neg_re)) {
|
|
366
|
+
std::string op = match[1].str();
|
|
367
|
+
std::string dst = match[3].str();
|
|
368
|
+
std::string src = match[4].str();
|
|
369
|
+
|
|
370
|
+
if (op == "neg") return get_indent() + dst + " = -" + src + ";";
|
|
371
|
+
if (op == "not") return get_indent() + dst + " = ~" + src + ";";
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return "";
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
std::string SmaliToJava::convert_cast(const std::string& line) {
|
|
378
|
+
// check-cast v0, Lcom/example/Class;
|
|
379
|
+
// int-to-long v0, v1
|
|
380
|
+
std::regex check_cast_re(R"(check-cast\s+(\w+),\s*(\S+))");
|
|
381
|
+
std::regex conv_re(R"((\w+)-to-(\w+)\s+(\w+),\s*(\w+))");
|
|
382
|
+
std::smatch match;
|
|
383
|
+
|
|
384
|
+
if (std::regex_search(line, match, check_cast_re)) {
|
|
385
|
+
std::string reg = match[1].str();
|
|
386
|
+
std::string type = type_to_java(match[2].str());
|
|
387
|
+
return get_indent() + reg + " = (" + type + ") " + reg + ";";
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (std::regex_search(line, match, conv_re)) {
|
|
391
|
+
std::string to_type = match[2].str();
|
|
392
|
+
std::string dst = match[3].str();
|
|
393
|
+
std::string src = match[4].str();
|
|
394
|
+
return get_indent() + dst + " = (" + to_type + ") " + src + ";";
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return "";
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
std::string SmaliToJava::convert_array(const std::string& line) {
|
|
401
|
+
// aget v0, v1, v2
|
|
402
|
+
// aput v0, v1, v2
|
|
403
|
+
std::regex aget_re(R"(aget(?:-\w+)?\s+(\w+),\s*(\w+),\s*(\w+))");
|
|
404
|
+
std::regex aput_re(R"(aput(?:-\w+)?\s+(\w+),\s*(\w+),\s*(\w+))");
|
|
405
|
+
std::regex array_len_re(R"(array-length\s+(\w+),\s*(\w+))");
|
|
406
|
+
std::smatch match;
|
|
407
|
+
|
|
408
|
+
if (std::regex_search(line, match, aget_re)) {
|
|
409
|
+
return get_indent() + match[1].str() + " = " + match[2].str() + "[" + match[3].str() + "];";
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (std::regex_search(line, match, aput_re)) {
|
|
413
|
+
return get_indent() + match[2].str() + "[" + match[3].str() + "] = " + match[1].str() + ";";
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (std::regex_search(line, match, array_len_re)) {
|
|
417
|
+
return get_indent() + match[1].str() + " = " + match[2].str() + ".length;";
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return "";
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
std::string SmaliToJava::convert_instruction(const std::string& line) {
|
|
424
|
+
std::string trimmed = trim(line);
|
|
425
|
+
if (trimmed.empty() || trimmed[0] == '#') return "";
|
|
426
|
+
|
|
427
|
+
// Handle offset prefix like ".0000: sget-object ..."
|
|
428
|
+
if (trimmed[0] == '.' && trimmed.size() > 5 && trimmed[5] == ':') {
|
|
429
|
+
trimmed = trim(trimmed.substr(6));
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Skip directives except method/class headers
|
|
433
|
+
if (trimmed[0] == '.') {
|
|
434
|
+
if (trimmed.find(".method") == 0) {
|
|
435
|
+
// Extract method signature
|
|
436
|
+
size_t name_start = trimmed.rfind(' ');
|
|
437
|
+
if (name_start != std::string::npos) {
|
|
438
|
+
std::string sig = trimmed.substr(name_start + 1);
|
|
439
|
+
size_t paren = sig.find('(');
|
|
440
|
+
std::string name = (paren != std::string::npos) ? sig.substr(0, paren) : sig;
|
|
441
|
+
return "\n" + get_indent() + "// Method: " + name;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (trimmed.find(".end method") == 0) {
|
|
445
|
+
return get_indent() + "}\n";
|
|
446
|
+
}
|
|
447
|
+
if (trimmed.find(".registers") == 0 || trimmed.find(".locals") == 0) {
|
|
448
|
+
return get_indent() + "{";
|
|
449
|
+
}
|
|
450
|
+
return "";
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Labels
|
|
454
|
+
if (trimmed[0] == ':') {
|
|
455
|
+
return get_indent() + trimmed.substr(1) + ":";
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Convert instructions
|
|
459
|
+
std::string result;
|
|
460
|
+
|
|
461
|
+
if (trimmed.find("const") == 0) result = convert_const(trimmed);
|
|
462
|
+
else if (trimmed.find("move") == 0) result = convert_move(trimmed);
|
|
463
|
+
else if (trimmed.find("invoke") == 0) result = convert_invoke(trimmed);
|
|
464
|
+
else if (trimmed.find("iget") == 0 || trimmed.find("sget") == 0 ||
|
|
465
|
+
trimmed.find("iput") == 0 || trimmed.find("sput") == 0)
|
|
466
|
+
result = convert_field_access(trimmed);
|
|
467
|
+
else if (trimmed.find("return") == 0) result = convert_return(trimmed);
|
|
468
|
+
else if (trimmed.find("if-") == 0) result = convert_if(trimmed);
|
|
469
|
+
else if (trimmed.find("new-") == 0) result = convert_new(trimmed);
|
|
470
|
+
else if (trimmed.find("aget") == 0 || trimmed.find("aput") == 0 ||
|
|
471
|
+
trimmed.find("array-length") == 0) result = convert_array(trimmed);
|
|
472
|
+
else if (trimmed.find("check-cast") == 0 || trimmed.find("-to-") != std::string::npos)
|
|
473
|
+
result = convert_cast(trimmed);
|
|
474
|
+
else if (trimmed.find("add-") == 0 || trimmed.find("sub-") == 0 ||
|
|
475
|
+
trimmed.find("mul-") == 0 || trimmed.find("div-") == 0 ||
|
|
476
|
+
trimmed.find("rem-") == 0 || trimmed.find("and-") == 0 ||
|
|
477
|
+
trimmed.find("or-") == 0 || trimmed.find("xor-") == 0 ||
|
|
478
|
+
trimmed.find("shl-") == 0 || trimmed.find("shr-") == 0 ||
|
|
479
|
+
trimmed.find("neg-") == 0 || trimmed.find("not-") == 0)
|
|
480
|
+
result = convert_arithmetic(trimmed);
|
|
481
|
+
else if (trimmed == "nop") return "";
|
|
482
|
+
else if (trimmed.find("goto") == 0) {
|
|
483
|
+
size_t colon = trimmed.find(':');
|
|
484
|
+
if (colon != std::string::npos) {
|
|
485
|
+
return get_indent() + "goto " + trimmed.substr(colon + 1) + ";";
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
else if (trimmed.find("throw") == 0) {
|
|
489
|
+
std::regex throw_re(R"(throw\s+(\w+))");
|
|
490
|
+
std::smatch match;
|
|
491
|
+
if (std::regex_search(trimmed, match, throw_re)) {
|
|
492
|
+
return get_indent() + "throw " + match[1].str() + ";";
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (result.empty() && !trimmed.empty()) {
|
|
497
|
+
return get_indent() + "// " + trimmed;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return result;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
std::string SmaliToJava::convert_method(const std::string& method_smali) {
|
|
504
|
+
registers_.clear();
|
|
505
|
+
indent_ = 1;
|
|
506
|
+
|
|
507
|
+
std::stringstream ss(method_smali);
|
|
508
|
+
std::string line;
|
|
509
|
+
std::stringstream result;
|
|
510
|
+
|
|
511
|
+
while (std::getline(ss, line)) {
|
|
512
|
+
std::string converted = convert_instruction(line);
|
|
513
|
+
if (!converted.empty()) {
|
|
514
|
+
result << converted << "\n";
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return result.str();
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
std::string SmaliToJava::convert(const std::string& smali_code) {
|
|
522
|
+
registers_.clear();
|
|
523
|
+
indent_ = 0;
|
|
524
|
+
|
|
525
|
+
std::stringstream ss(smali_code);
|
|
526
|
+
std::string line;
|
|
527
|
+
std::stringstream result;
|
|
528
|
+
|
|
529
|
+
bool in_method = false;
|
|
530
|
+
|
|
531
|
+
while (std::getline(ss, line)) {
|
|
532
|
+
std::string trimmed = trim(line);
|
|
533
|
+
|
|
534
|
+
if (trimmed.find(".class") == 0) {
|
|
535
|
+
// Extract class name
|
|
536
|
+
size_t last_space = trimmed.rfind(' ');
|
|
537
|
+
if (last_space != std::string::npos) {
|
|
538
|
+
std::string cls = type_to_java(trimmed.substr(last_space + 1));
|
|
539
|
+
result << "// Decompiled from Smali\n";
|
|
540
|
+
result << "class " << cls << " {\n";
|
|
541
|
+
indent_ = 1;
|
|
542
|
+
}
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (trimmed.find(".super") == 0) {
|
|
547
|
+
size_t last_space = trimmed.rfind(' ');
|
|
548
|
+
if (last_space != std::string::npos) {
|
|
549
|
+
std::string super_cls = type_to_java(trimmed.substr(last_space + 1));
|
|
550
|
+
result << " // extends " << super_cls << "\n\n";
|
|
551
|
+
}
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (trimmed.find(".method") == 0) {
|
|
556
|
+
in_method = true;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (in_method) {
|
|
560
|
+
std::string converted = convert_instruction(line);
|
|
561
|
+
if (!converted.empty()) {
|
|
562
|
+
result << converted << "\n";
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (trimmed.find(".end method") == 0) {
|
|
567
|
+
in_method = false;
|
|
568
|
+
result << "\n";
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
result << "}\n";
|
|
573
|
+
return result.str();
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
} // namespace dex
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <vector>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
|
|
7
|
+
namespace apk {
|
|
8
|
+
|
|
9
|
+
struct FileEntry {
|
|
10
|
+
std::string name;
|
|
11
|
+
std::vector<uint8_t> data;
|
|
12
|
+
bool is_directory;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
class ApkHandler {
|
|
16
|
+
public:
|
|
17
|
+
ApkHandler() = default;
|
|
18
|
+
~ApkHandler() = default;
|
|
19
|
+
|
|
20
|
+
bool open(const std::string& path);
|
|
21
|
+
bool create(const std::string& path); // Create new empty APK
|
|
22
|
+
bool save(const std::string& path);
|
|
23
|
+
void close();
|
|
24
|
+
|
|
25
|
+
std::vector<std::string> list_files() const;
|
|
26
|
+
bool extract_file(const std::string& name, std::vector<uint8_t>& data) const;
|
|
27
|
+
bool replace_file(const std::string& name, const std::vector<uint8_t>& data);
|
|
28
|
+
bool add_file(const std::string& name, const std::vector<uint8_t>& data);
|
|
29
|
+
bool delete_file(const std::string& name);
|
|
30
|
+
void remove_files_by_pattern(const std::string& pattern);
|
|
31
|
+
|
|
32
|
+
bool is_open() const { return is_open_; }
|
|
33
|
+
std::string get_path() const { return path_; }
|
|
34
|
+
|
|
35
|
+
private:
|
|
36
|
+
std::string path_;
|
|
37
|
+
std::vector<FileEntry> entries_;
|
|
38
|
+
bool is_open_ = false;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
} // namespace apk
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <vector>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <functional>
|
|
7
|
+
|
|
8
|
+
namespace apk {
|
|
9
|
+
|
|
10
|
+
struct ZipEntry {
|
|
11
|
+
std::string name;
|
|
12
|
+
uint32_t compressed_size;
|
|
13
|
+
uint32_t uncompressed_size;
|
|
14
|
+
uint32_t crc32;
|
|
15
|
+
uint16_t compression_method;
|
|
16
|
+
uint32_t local_header_offset;
|
|
17
|
+
std::vector<uint8_t> data;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
class ZipReader {
|
|
21
|
+
public:
|
|
22
|
+
bool open(const std::string& path);
|
|
23
|
+
bool open(const std::vector<uint8_t>& data);
|
|
24
|
+
void close();
|
|
25
|
+
|
|
26
|
+
std::vector<std::string> list() const;
|
|
27
|
+
bool extract(const std::string& name, std::vector<uint8_t>& out) const;
|
|
28
|
+
bool extract_all(std::function<void(const std::string&, const std::vector<uint8_t>&)> callback) const;
|
|
29
|
+
|
|
30
|
+
private:
|
|
31
|
+
std::vector<ZipEntry> entries_;
|
|
32
|
+
std::vector<uint8_t> data_;
|
|
33
|
+
bool is_open_ = false;
|
|
34
|
+
|
|
35
|
+
bool parse_central_directory();
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
class ZipWriter {
|
|
39
|
+
public:
|
|
40
|
+
void add_file(const std::string& name, const std::vector<uint8_t>& data, bool compress = true);
|
|
41
|
+
void add_stored(const std::string& name, const std::vector<uint8_t>& data);
|
|
42
|
+
bool save(const std::string& path);
|
|
43
|
+
std::vector<uint8_t> finalize();
|
|
44
|
+
|
|
45
|
+
private:
|
|
46
|
+
struct Entry {
|
|
47
|
+
std::string name;
|
|
48
|
+
std::vector<uint8_t> compressed_data;
|
|
49
|
+
uint32_t uncompressed_size;
|
|
50
|
+
uint32_t crc32;
|
|
51
|
+
uint16_t compression_method;
|
|
52
|
+
uint32_t local_header_offset;
|
|
53
|
+
};
|
|
54
|
+
std::vector<Entry> entries_;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
} // namespace apk
|