1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
use std::{fs, path::Path, process::Command};
use abxml::apk::Apk;
use colored::Colorize;
use failure::{bail, format_err, Error, ResultExt};
use crate::{get_package_name, print_warning, Config};
pub fn decompress<P: AsRef<Path>>(config: &mut Config, package: P) -> Result<(), Error> {
let path = config
.dist_folder()
.join(package.as_ref().file_stem().unwrap());
if !path.exists() || config.is_force() {
if path.exists() {
if config.is_verbose() {
println!("The application decompression folder existed. But no more!");
}
if let Err(e) = fs::remove_dir_all(&path) {
print_warning(format!(
"there was an error when removing the decompression folder: {}",
e
));
}
}
config.set_force();
if config.is_verbose() {
println!();
println!("Decompressing the application.");
}
let mut apk = Apk::from_path(package.as_ref()).context("error loading apk file")?;
apk.export(&path, true).context(format_err!(
"could not decompress the apk file. Tried to decompile at: {}",
path.display()
))?;
if config.is_verbose() {
println!(
"{}",
format!(
"The application has been decompressed in {}.",
path.display()
)
.green()
);
} else if !config.is_quiet() {
println!("Application decompressed.");
}
} else if config.is_verbose() {
println!(
"Seems that the application has already been decompressed. There is no need to do it \
again."
);
} else {
println!("Skipping decompression.");
}
Ok(())
}
pub fn dex_to_jar<P: AsRef<Path>>(config: &mut Config, package: P) -> Result<(), Error> {
let package_name = get_package_name(package.as_ref());
let classes = config.dist_folder().join(&package_name).join("classes.jar");
if config.is_force() || !classes.exists() {
config.set_force();
let output = Command::new(config.dex2jar_folder().join(
if cfg!(target_family = "windows") {
"d2j-dex2jar.bat"
} else {
"d2j-dex2jar.sh"
},
))
.arg(config.dist_folder().join(&package_name).join("classes.dex"))
.arg("-f")
.arg("-o")
.arg(&classes)
.output()
.context(format_err!(
"there was an error when executing the {} to {} conversion command",
".dex".italic(),
".jar".italic()
))?;
let stderr = String::from_utf8_lossy(&output.stderr);
let mut call_ok = output.status.success() || !stderr.contains("use");
if stderr.find('\n') != Some(stderr.len() - 1) {
if stderr.starts_with("Picked up _JAVA_OPTIONS:") {
call_ok = stderr.lines().count() == 2;
} else {
call_ok = false;
}
}
if !call_ok {
bail!(
"the {} to {} conversion command returned an error. More info: {}",
".dex".italic(),
".jar".italic(),
stderr
);
}
if config.is_verbose() {
println!(
"{}",
format!(
"The application {} {} {}",
".jar".italic(),
"file has been generated in".green(),
format!("{}", classes.display()).green()
)
.green()
);
} else if !config.is_quiet() {
println!("Jar file generated.");
}
} else if config.is_verbose() {
println!(
"Seems that there is already a {} file for the application. There is no need to \
create it again.",
".jar".italic()
);
} else {
println!("Skipping {} file generation.", ".jar".italic());
}
Ok(())
}
pub fn decompile<P: AsRef<Path>>(config: &mut Config, package: P) -> Result<(), Error> {
let package_name = get_package_name(package.as_ref());
let out_path = config.dist_folder().join(&package_name).join("classes");
if config.is_force() || !out_path.exists() {
config.set_force();
let output = Command::new("java")
.arg("-jar")
.arg(config.jd_cmd_file())
.arg(config.dist_folder().join(&package_name).join("classes.jar"))
.arg("-od")
.arg(&out_path)
.output()
.context("there was an unknown error decompiling the application")?;
if !output.status.success() {
bail!(
"the decompilation command returned an error. More info:\n{}",
String::from_utf8_lossy(&output.stdout)
);
}
if config.is_verbose() {
println!(
"{}",
"The application has been successfully decompiled!".green()
);
} else if !config.is_quiet() {
println!("Application decompiled.");
}
} else if config.is_verbose() {
println!(
"Seems that there is already a source folder for the application. There is no need to \
decompile it again."
);
} else {
println!("Skipping decompilation.");
}
Ok(())
}