├── .gitignore
├── README.md
├── image
└── WX20191207-171805@2x.png
├── pom.xml
└── src
└── main
├── java
└── xpp
│ └── apk2jar
│ ├── BaseUtils.java
│ ├── FxViewUtil.java
│ ├── Main.java
│ └── ZipUtils.java
└── resources
├── apktool-2.4.1.zip
├── dex2jar-2.0.zip
├── icon.png
└── jd-gui-1.6.5.jar
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Android ###
2 | # Built application files
3 | *.apk
4 | *.ap_
5 |
6 | # Files for the Dalvik VM
7 | *.dex
8 |
9 | # Java class files
10 | *.class
11 | *.db
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | target/
18 | src/mac/
19 | src/windows/
20 |
21 | # Gradle files
22 | .gradle/
23 | build/
24 | gradle/
25 | gradlew
26 | gradlew.bat
27 |
28 | # Local configuration file (sdk path, etc)
29 | local.properties
30 |
31 | # Proguard folder generated by Eclipse
32 | proguard/
33 |
34 | # Log Files
35 | *.log
36 |
37 |
38 | #Android Studio
39 | .idea
40 | classes
41 |
42 | app/app.iml
43 | AppMaker.iml
44 |
45 | .DS_Store
46 | /build
47 | /captures
48 | *.iml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ApkDex2jar
2 |
3 | 
4 |
5 | ### 说明
6 | 基于Dex2jar和apktool、jd-gui等第三方工具,实现对apk进行快速反编译操作。
7 |
8 | 简化反编译过程,告别繁琐的文件夹切换操作,一拖即可反编译。
9 |
10 | 本工具仅用于辅助研发测试,或普通的apk反编译。
11 | 对于已使用第三方加固过的apk,可能会反编译失败,或者无法进行反编译。
12 |
13 |
14 | ### 环境
15 | * UI界面基于java8+javafx
16 | * IDEA
17 |
18 | ### 工具版本
19 | * Dex2jar:2.0
20 | * apktool:2.4.1
21 | * JD-GUI:1.6.5
22 |
23 | 以上工具已经内置到程序中,无需再设置
24 |
25 |
26 | ### 使用
27 | * 运行后将apk直接拖入到程序界面。等待反编译完成。
28 | * 反编译完成后,会在apk所在的同级文件夹下生成一个以_ApkDex2jar结尾的文件夹。反编译后的jar及资源文件保存在此文件夹中。
29 | * 点击"查看反编译文件"会打开反编译后对应的文件夹。
30 | * 点击JD-GUI图标会打开Java Decompiler,将上一步中的classes.dex.jar文件拖入其中,即可查看对应的java代码。
--------------------------------------------------------------------------------
/image/WX20191207-171805@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadisting/ApkDex2jar/a23742108915a4661a42d64d703998954aee7a25/image/WX20191207-171805@2x.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | groupId
8 | ApkDex2jar
9 | 1.0-SNAPSHOT
10 |
11 |
12 | 1.8
13 | 1.8
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/main/java/xpp/apk2jar/BaseUtils.java:
--------------------------------------------------------------------------------
1 | package xpp.apk2jar;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileOutputStream;
6 | import java.io.InputStream;
7 | import java.nio.file.Files;
8 | import java.nio.file.Path;
9 | import java.nio.file.Paths;
10 | import java.nio.file.attribute.PosixFilePermission;
11 | import java.util.HashSet;
12 | import java.util.Set;
13 |
14 | public class BaseUtils {
15 |
16 | public static void moveDirAndDelete(String oldPath, String newPath) {
17 | File oldPathFile = new File(oldPath);
18 | if (!oldPathFile.exists()) {
19 | return;
20 | }
21 | File newPathFile = new File(newPath);
22 | if (!newPathFile.exists()) {
23 | newPathFile.mkdirs();
24 | }
25 | try {
26 | String[] file = oldPathFile.list();
27 | // 要注意,这个temp仅仅是一个临时文件指针
28 | // 整个程序并没有创建临时文件
29 | File temp = null;
30 | for (int i = 0; i < file.length; i++) {
31 | // 如果oldPath以路径分隔符/或者\结尾,那么则oldPath/文件名就可以了
32 | // 否则要自己oldPath后面补个路径分隔符再加文件名
33 | // 谁知道你传递过来的参数是f:/a还是f:/a/啊?
34 | if (oldPath.endsWith(File.separator)) {
35 | temp = new File(oldPath + file[i]);
36 | } else {
37 | temp = new File(oldPath + File.separator + file[i]);
38 | }
39 |
40 | // 如果游标遇到文件
41 | if (temp.isFile()) {
42 | FileInputStream input = new FileInputStream(temp);
43 | // 复制并且改名
44 | FileOutputStream output = new FileOutputStream(newPath
45 | + "/" + "rename_" + (temp.getName()).toString());
46 | byte[] bufferarray = new byte[1024 * 64];
47 | int prereadlength;
48 | while ((prereadlength = input.read(bufferarray)) != -1) {
49 | output.write(bufferarray, 0, prereadlength);
50 | }
51 | output.flush();
52 | output.close();
53 | input.close();
54 | }
55 | // 如果游标遇到文件夹
56 | if (temp.isDirectory()) {
57 | moveDirAndDelete(oldPath + "/" + file[i], newPath + "/" + file[i]);
58 | }
59 | }
60 | } catch (Exception e) {
61 | System.out.println("复制整个文件夹内容操作出错");
62 | e.printStackTrace();
63 | }
64 | }
65 |
66 | public static void moveAndDelete(File oldFile, File newFile) {
67 | if (newFile.exists()) {
68 | newFile.delete();
69 | }
70 | BaseUtils.copyFile(oldFile, newFile);
71 | oldFile.delete();
72 | }
73 |
74 | public static void deleteDir(File dir) {
75 | if (dir == null || !dir.exists()) {
76 | return;
77 | }
78 | if (dir.isDirectory()) {
79 | File[] files = dir.listFiles();
80 | if (files != null && files.length > 0) {
81 | for (File f : files) {
82 | if (f.isFile()) {
83 | f.delete();
84 | } else {
85 | deleteDir(f);
86 | }
87 | }
88 | }
89 | dir.delete();
90 | } else {
91 | dir.delete();
92 | }
93 | }
94 |
95 |
96 | public static void copyFile(File oldFile, File newFile) {
97 | try {
98 | int bytesum = 0;
99 | int byteread = 0;
100 | InputStream inStream = new FileInputStream(oldFile); //读入原文件
101 | FileOutputStream fs = new FileOutputStream(newFile);
102 | byte[] buffer = new byte[1444];
103 | while ((byteread = inStream.read(buffer)) != -1) {
104 | bytesum += byteread; //字节数 文件大小
105 | fs.write(buffer, 0, byteread);
106 | }
107 | inStream.close();
108 | fs.close();
109 | oldFile.delete();
110 | } catch (Exception e) {
111 | e.printStackTrace();
112 | }
113 | }
114 |
115 | public static void copyFile(InputStream inStream, File newFile) {
116 | try {
117 | int bytesum = 0;
118 | int byteread = 0;
119 | FileOutputStream fs = new FileOutputStream(newFile);
120 | byte[] buffer = new byte[1444];
121 | while ((byteread = inStream.read(buffer)) != -1) {
122 | bytesum += byteread; //字节数 文件大小
123 | fs.write(buffer, 0, byteread);
124 | }
125 | inStream.close();
126 | fs.close();
127 | } catch (Exception e) {
128 | e.printStackTrace();
129 | }
130 | }
131 |
132 |
133 | public static File getLocalBinPath() {
134 |
135 | File file = new File(System.getProperty("user.home") + "/.ApkDex2Jar/bin/");
136 | if (!file.exists()) {
137 | file.mkdirs();
138 | }
139 | return file;
140 | }
141 |
142 |
143 | public static void initBin() {
144 |
145 | getDex2JarShellPath();
146 | getApktoolShellPath();
147 | getJdJuiPath();
148 |
149 | changeFolderPermission(BaseUtils.getLocalBinPath());
150 | }
151 |
152 | public static String getDex2JarShellPath() {
153 |
154 | String path;
155 |
156 | File dex2jarDir = new File(BaseUtils.getLocalBinPath(), "dex2jar");
157 | if (!dex2jarDir.exists()) {
158 | dex2jarDir.mkdirs();
159 | }
160 | File vDir = new File(dex2jarDir, "dex2jar-2.0");
161 | File shFile = new File(vDir, "d2j-dex2jar.sh");
162 | if (vDir.exists() && shFile.exists()) {
163 | path = shFile.getPath();
164 | } else {
165 | File zipFile = new File(dex2jarDir, "dex2jar-2.0.zip");
166 | InputStream resourceAsStream = Main.class.getResourceAsStream("/dex2jar-2.0.zip");
167 | BaseUtils.copyFile(resourceAsStream, zipFile);
168 | ZipUtils.unZip2CurrentDir(zipFile.getPath());
169 | File filed = new File(zipFile.getPath().replace(".zip", ""));
170 | File shpath = new File(filed, "d2j-dex2jar.sh");
171 | zipFile.delete();
172 | path = shpath.getPath();
173 | }
174 |
175 | return path;
176 | }
177 |
178 | public static String getApktoolShellPath() {
179 |
180 | String path;
181 |
182 | File apktoolDir = new File(BaseUtils.getLocalBinPath(), "apktool");
183 | if (!apktoolDir.exists()) {
184 | apktoolDir.mkdirs();
185 | }
186 | File vDir = new File(apktoolDir, "apktool-2.4.1");
187 | File shFile = new File(vDir, "apktool");
188 | if (vDir.exists() && shFile.exists()) {
189 | path = shFile.getPath();
190 | } else {
191 | File zipFile = new File(apktoolDir, "apktool-2.4.1.zip");
192 | InputStream resourceAsStream = Main.class.getResourceAsStream("/apktool-2.4.1.zip");
193 | BaseUtils.copyFile(resourceAsStream, zipFile);
194 | ZipUtils.unZip2CurrentDir(zipFile.getPath());
195 | File filed = new File(zipFile.getPath().replace(".zip", ""));
196 | File shpath = new File(filed, "apktool");
197 | zipFile.delete();
198 | path = shpath.getPath();
199 | }
200 |
201 | return path;
202 | }
203 |
204 | public static String getJdJuiPath() {
205 |
206 | String path;
207 |
208 | File gui = new File(BaseUtils.getLocalBinPath(), "jd-gui-1.6.5.jar");
209 | if (gui.exists()) {
210 | path = gui.getPath();
211 | } else {
212 | File guiFile = new File(BaseUtils.getLocalBinPath(), "jd-gui-1.6.5.jar");
213 | InputStream resourceAsStream = Main.class.getResourceAsStream("/jd-gui-1.6.5.jar");
214 | BaseUtils.copyFile(resourceAsStream, guiFile);
215 | path = guiFile.getPath();
216 | }
217 |
218 | return path;
219 | }
220 |
221 |
222 | private static void changeFolderPermission(File dirFile) {
223 | Set perms = new HashSet<>();
224 | perms.add(PosixFilePermission.OWNER_READ);
225 | perms.add(PosixFilePermission.OWNER_WRITE);
226 | perms.add(PosixFilePermission.OWNER_EXECUTE);
227 | perms.add(PosixFilePermission.GROUP_READ);
228 | perms.add(PosixFilePermission.GROUP_WRITE);
229 | perms.add(PosixFilePermission.GROUP_EXECUTE);
230 | changeAllPermission(dirFile, perms);
231 | }
232 |
233 | private static void changeAllPermission(File file, Set perms) {
234 |
235 | if (file.isFile()) {
236 | changePermission(file, perms);
237 | } else {
238 | changePermission(file, perms);
239 | File[] files = file.listFiles();
240 | for (File f : files) {
241 | if (f.isFile()) {
242 | changePermission(f, perms);
243 | } else {
244 | changeAllPermission(f, perms);
245 | }
246 | }
247 | }
248 | }
249 |
250 |
251 | private static void changePermission(File file, Set perms) {
252 |
253 | try {
254 | Path path = Paths.get(file.getAbsolutePath());
255 | Files.setPosixFilePermissions(path, perms);
256 | } catch (Exception e) {
257 | e.printStackTrace();
258 | }
259 | }
260 |
261 |
262 | public static boolean unPressApk(File apkFile, File dir) {
263 | String command = BaseUtils.getApktoolShellPath() + " d -f -o " + dir.getPath() + "_ApkDex2jar " + apkFile.getPath();
264 | return execCommand(command);
265 | }
266 |
267 |
268 | public static boolean dex2jar(File dexFile) {
269 |
270 | File newFile = new File(dexFile.getParentFile(), dexFile.getName() + ".jar");
271 | String command = BaseUtils.getDex2JarShellPath() + " -f -o " + newFile.getPath() + " " + dexFile.getPath();
272 | return execCommand(command);
273 | }
274 |
275 |
276 |
277 | public static boolean execCommand(String command){
278 | try {
279 | Runtime.getRuntime().exec(command).waitFor();
280 | return true;
281 | } catch (Exception e) {
282 | e.printStackTrace();
283 | }
284 | return false;
285 | }
286 |
287 |
288 |
289 | }
290 |
--------------------------------------------------------------------------------
/src/main/java/xpp/apk2jar/FxViewUtil.java:
--------------------------------------------------------------------------------
1 | package xpp.apk2jar;
2 |
3 | import java.awt.Desktop;
4 | import java.net.URI;
5 |
6 | import javafx.geometry.Insets;
7 | import javafx.scene.control.Button;
8 | import javafx.scene.layout.Background;
9 | import javafx.scene.layout.BackgroundFill;
10 | import javafx.scene.layout.Border;
11 | import javafx.scene.layout.BorderStroke;
12 | import javafx.scene.layout.BorderStrokeStyle;
13 | import javafx.scene.layout.BorderWidths;
14 | import javafx.scene.layout.CornerRadii;
15 | import javafx.scene.layout.Region;
16 | import javafx.scene.paint.Color;
17 | import javafx.scene.paint.Paint;
18 |
19 | /**
20 | * Created by xupanpan on 3/17/17.
21 | */
22 | public class FxViewUtil {
23 |
24 |
25 | public static Background getDefBackground() {
26 |
27 | return getBackground(Color.TRANSPARENT, CornerRadii.EMPTY, Insets.EMPTY);
28 | }
29 |
30 | public static Background getBackground(Paint color) {
31 |
32 | return getBackground(color, CornerRadii.EMPTY, Insets.EMPTY);
33 | }
34 |
35 | public static Background getBackground(Paint color, int corner) {
36 |
37 | return getBackground(color, new CornerRadii(corner), Insets.EMPTY);
38 | }
39 |
40 | public static Background getBackground(Paint color, CornerRadii corner) {
41 |
42 | return getBackground(color, corner, Insets.EMPTY);
43 | }
44 |
45 |
46 | public static Background getBackground(Paint color, CornerRadii corner, Insets insets) {
47 | return new Background(new BackgroundFill(color, corner, insets));
48 | }
49 |
50 |
51 | public static Border getDefBorder() {
52 |
53 | return getBorder(Color.WHITE, 5, 1);
54 | }
55 |
56 | public static Border getBorder(Paint color) {
57 |
58 | return getBorder(color, 5, 1);
59 | }
60 |
61 | public static Border getBorder(Paint color, double corner, double borderWidths) {
62 |
63 | return getBorder(color, BorderStrokeStyle.SOLID, new CornerRadii(corner), new BorderWidths(borderWidths));
64 | }
65 |
66 |
67 | public static Border getBorder(Paint color, BorderStrokeStyle style, CornerRadii corner, BorderWidths borderWidths) {
68 | return new Border(new BorderStroke(color, style, corner, borderWidths));
69 | }
70 |
71 |
72 | public static void setDefTextFileStyle(Region textField) {
73 | textField.setBackground(FxViewUtil.getDefBackground());
74 | textField.setBorder(FxViewUtil.getBorder(Color.WHITE, 3, 1));
75 | }
76 |
77 | public static void setDefButtonStyle(Button btn) {
78 | setDefTextFileStyle(btn);
79 | btn.setTextFill(Color.WHITE);
80 | }
81 |
82 | public static void setButtonBlueStyle(Button btn) {
83 | btn.setBackground(FxViewUtil.getDefBackground());
84 | btn.setBorder(FxViewUtil.getBorder(Color.LIGHTSKYBLUE, 3, 1));
85 | btn.setTextFill(Color.LIGHTSKYBLUE);
86 | }
87 |
88 |
89 | public static void openSystemBrowser(String url) {
90 |
91 | if (Desktop.isDesktopSupported()) {
92 | try {
93 | URI uri = URI.create(url);
94 | Desktop desktop = Desktop.getDesktop();
95 | if (desktop.isSupported(Desktop.Action.BROWSE)) {
96 | desktop.browse(uri);
97 | }
98 |
99 | } catch (Exception e) {
100 | e.printStackTrace();
101 | }
102 | }
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/xpp/apk2jar/Main.java:
--------------------------------------------------------------------------------
1 | package xpp.apk2jar;
2 |
3 | import javafx.application.Application;
4 | import javafx.application.Platform;
5 | import javafx.event.ActionEvent;
6 | import javafx.event.EventHandler;
7 | import javafx.geometry.Insets;
8 | import javafx.geometry.Pos;
9 | import javafx.scene.Scene;
10 | import javafx.scene.control.Button;
11 | import javafx.scene.control.Label;
12 | import javafx.scene.control.Separator;
13 | import javafx.scene.image.Image;
14 | import javafx.scene.image.ImageView;
15 | import javafx.scene.input.DragEvent;
16 | import javafx.scene.input.Dragboard;
17 | import javafx.scene.input.TransferMode;
18 | import javafx.scene.layout.Background;
19 | import javafx.scene.layout.BorderPane;
20 | import javafx.scene.layout.HBox;
21 | import javafx.scene.layout.VBox;
22 | import javafx.scene.paint.Color;
23 | import javafx.scene.text.Font;
24 | import javafx.stage.FileChooser;
25 | import javafx.stage.Stage;
26 |
27 | import java.io.File;
28 | import java.io.IOException;
29 | import java.util.ArrayList;
30 | import java.util.List;
31 | import java.util.concurrent.ExecutorService;
32 | import java.util.concurrent.Executors;
33 |
34 | public class Main extends Application {
35 | private VBox centerBox;
36 | private static final double WIDTH = 600d;
37 | private static final double HIGTH = 500d;
38 | private Label label, statusLabel;
39 | private StringBuilder sb = new StringBuilder();
40 | private ExecutorService executorService;
41 | BorderPane borderPane;
42 | private Stage mStage;
43 |
44 | public static void main(String[] args) {
45 | launch(args);
46 | }
47 |
48 | private List apkList;
49 |
50 |
51 | @Override
52 | public void start(Stage primaryStage) {
53 | mStage = primaryStage;
54 | borderPane = new BorderPane();
55 | Scene scene = new Scene(borderPane, WIDTH, HIGTH);
56 | primaryStage.setWidth(WIDTH);
57 | primaryStage.setHeight(HIGTH);
58 | primaryStage.setResizable(false);
59 | primaryStage.setScene(scene);
60 | primaryStage.show();
61 |
62 | initView();
63 | BaseUtils.initBin();
64 | }
65 |
66 |
67 | private void initView() {
68 |
69 | HBox topBox = new HBox(8);
70 | topBox.setAlignment(Pos.CENTER_LEFT);
71 | topBox.setBackground(FxViewUtil.getBackground(Color.valueOf("8F8F8F")));
72 | topBox.setPadding(new Insets(10, 10, 10, 10));
73 | Button chooseApk = new Button("选择apk文件");
74 | chooseApk.setBackground(FxViewUtil.getBackground(Color.valueOf("DCDCDC")));
75 | chooseApk.setOnAction(new EventHandler() {
76 | @Override
77 | public void handle(ActionEvent event) {
78 | FileChooser chooser = new FileChooser();
79 | chooser.getExtensionFilters().addAll(
80 | new FileChooser.ExtensionFilter("可以选择多个apk", "*.apk")
81 | );
82 | List files = chooser.showOpenMultipleDialog(mStage);
83 | findApk(files);
84 | }
85 | });
86 | Button openDir = new Button("查看反编译文件");
87 | openDir.setBackground(FxViewUtil.getBackground(Color.valueOf("DCDCDC")));
88 | openDir.setOnAction(new EventHandler() {
89 | @Override
90 | public void handle(ActionEvent event) {
91 | if (apkList != null && apkList.size() > 0) {
92 | if (isExecute) {
93 | statusLabel.setText("请等待反编译完成!");
94 | } else {
95 | File file = apkList.get(0);
96 | if (file.exists()) {
97 | File ff = new File(file.getParentFile(), file.getName().replace(".apk", "_ApkDex2jar"));
98 | try {
99 | statusLabel.setText("");
100 | java.awt.Desktop.getDesktop().open(ff);
101 | } catch (IOException e) {
102 | e.printStackTrace();
103 | statusLabel.setText("查看反编译文件,打开失败");
104 | }
105 | } else {
106 | statusLabel.setText("apk文件不存在");
107 | }
108 | }
109 |
110 | } else {
111 | statusLabel.setText("请先选择要反编译的apk文件!");
112 | }
113 | }
114 | });
115 | Button openGUI = new Button();
116 | openGUI.setBackground(Background.EMPTY);
117 | openGUI.setOnAction(new EventHandler() {
118 | @Override
119 | public void handle(ActionEvent event) {
120 | try {
121 | java.awt.Desktop.getDesktop().open(new File(BaseUtils.getJdJuiPath()));
122 | } catch (IOException e) {
123 | e.printStackTrace();
124 | }
125 | }
126 | });
127 |
128 | ImageView lastBgView = new ImageView(new Image(Main.class.getResourceAsStream("/icon.png")));
129 | lastBgView.setFitHeight(20);
130 | lastBgView.setFitWidth(20);
131 | openGUI.setGraphic(lastBgView);
132 |
133 | topBox.getChildren().addAll(chooseApk, openDir, openGUI);
134 |
135 | new Separator();
136 |
137 | borderPane.setTop(topBox);
138 |
139 | centerBox = new VBox();
140 | centerBox.setPadding(new Insets(10, 10, 10, 10));
141 | centerBox.setSpacing(10);
142 | centerBox.setAlignment(Pos.TOP_LEFT);
143 | centerBox.setBackground(Background.EMPTY);
144 |
145 | label = new Label("请将apk文件拖动到此区域");
146 | label.setFont(Font.font(16));
147 | centerBox.getChildren().addAll(label);
148 |
149 | centerBox.setOnDragOver(new EventHandler() {
150 | public void handle(DragEvent event) {
151 | if (event.getGestureSource() != centerBox) {
152 | event.acceptTransferModes(TransferMode.ANY);
153 | }
154 | }
155 | });
156 |
157 | centerBox.setOnDragDropped(new EventHandler() {
158 | public void handle(DragEvent event) {
159 | if (isExecute) {
160 | return;
161 | }
162 | Dragboard dragboard = event.getDragboard();
163 | List files = dragboard.getFiles();
164 | findApk(files);
165 | }
166 | });
167 | borderPane.setCenter(centerBox);
168 |
169 | HBox bottomBox = new HBox();
170 | bottomBox.setPadding(new Insets(5, 5, 5, 5));
171 | statusLabel = new Label();
172 | statusLabel.setFont(Font.font(12));
173 | statusLabel.setTextFill(Color.valueOf("D81B60"));
174 | bottomBox.getChildren().addAll(statusLabel);
175 | borderPane.setBottom(bottomBox);
176 |
177 | }
178 |
179 | private void findApk(List files) {
180 | if (files != null && files.size() > 0) {
181 | if (apkList == null) {
182 | apkList = new ArrayList<>();
183 | } else {
184 | apkList.clear();
185 | }
186 | index = 0;
187 | apkList.addAll(files);
188 | executorService = Executors.newSingleThreadExecutor();
189 | parseList();
190 | }
191 | }
192 |
193 | private void showProgress() {
194 | Platform.runLater(new Runnable() {
195 | @Override
196 | public void run() {
197 | label.setText(sb.toString());
198 | }
199 | });
200 | }
201 |
202 |
203 | private void addProgressStr(String str) {
204 | sb.append(str + "\n");
205 | showProgress();
206 | }
207 |
208 |
209 | private int index = 0;
210 |
211 | private void parseList() {
212 |
213 | if (index < apkList.size()) {
214 | execute(apkList.get(index));
215 | } else {
216 | addProgressStr("=======执行完成!=========");
217 | isExecute = false;
218 | index = 0;
219 | executorService.shutdown();
220 | }
221 |
222 | }
223 |
224 |
225 | private boolean isExecute;
226 |
227 | private void execute(File apkFile) {
228 | sb.delete(0, sb.length());
229 |
230 | if (!apkFile.exists()) {
231 | addProgressStr("文件不存在!");
232 | stopExe();
233 | return;
234 | }
235 | if (apkFile.isDirectory() || !apkFile.getName().endsWith(".apk")) {
236 | addProgressStr(apkFile.getName() + "不是apk文件!");
237 | stopExe();
238 | return;
239 | }
240 |
241 | isExecute = true;
242 | executorService.execute(new Executer(apkFile));
243 | }
244 |
245 |
246 | private class Executer implements Runnable {
247 |
248 | private File apkFile;
249 |
250 | public Executer(File apkFile) {
251 | this.apkFile = apkFile;
252 | }
253 |
254 | @Override
255 | public void run() {
256 | addProgressStr("反编译:" + apkFile.getName());
257 |
258 | addProgressStr("1.开始解压缩");
259 | String unZipPath = ZipUtils.unZip(apkFile.getPath());
260 | if (unZipPath == null) {
261 | addProgressStr(" 解压缩失败!");
262 | stopExe();
263 | return;
264 | }
265 | addProgressStr(" 解压缩完成!");
266 |
267 | File dirFile = new File(unZipPath);
268 | if (!dirFile.exists()) {
269 | addProgressStr(" 解压后文件夹不存在!");
270 | stopExe();
271 | return;
272 | }
273 |
274 | File[] files = dirFile.listFiles();
275 | if (files == null || files.length == 0) {
276 | addProgressStr(" 解压后文件夹无文件!");
277 | stopExe();
278 | return;
279 | }
280 |
281 | List dexFileList = new ArrayList<>();
282 |
283 | for (File file : files) {
284 | if (file.isDirectory()) {
285 | BaseUtils.deleteDir(file);
286 | continue;
287 | }
288 | if (file.getName().endsWith(".dex")) {
289 | dexFileList.add(file);
290 | } else {
291 | file.delete();
292 | }
293 | }
294 | if (dexFileList.size() == 0) {
295 | addProgressStr(" 解压后文件夹无dex文件!");
296 | stopExe();
297 | return;
298 | } else {
299 | addProgressStr(" 解压出" + dexFileList.size() + "个dex文件");
300 | }
301 |
302 | addProgressStr("2.开始反编译......");
303 | for (int i = 0; i < dexFileList.size(); i++) {
304 | addProgressStr(" 正在反编译第" + (i + 1) + "个dex文件");
305 | BaseUtils.dex2jar(dexFileList.get(i));
306 | }
307 | addProgressStr(" dex反编译完成!");
308 |
309 | addProgressStr("3.解压res文件......");
310 |
311 | boolean b = BaseUtils.unPressApk(apkFile, dirFile);
312 | String unPressStatus = b ? "成功" : "失败";
313 | addProgressStr(" res解压" + unPressStatus);
314 | if (b) {
315 | BaseUtils.moveDirAndDelete(dirFile.getPath(), dirFile.getPath() + "_ApkDex2jar");
316 | BaseUtils.deleteDir(dirFile);
317 | }else {
318 |
319 | }
320 | stopExe();
321 | }
322 | }
323 |
324 | private void stopExe() {
325 | index++;
326 | parseList();
327 | }
328 |
329 | }
330 |
--------------------------------------------------------------------------------
/src/main/java/xpp/apk2jar/ZipUtils.java:
--------------------------------------------------------------------------------
1 | package xpp.apk2jar;
2 |
3 | import java.io.*;
4 | import java.util.Enumeration;
5 | import java.util.zip.ZipEntry;
6 | import java.util.zip.ZipFile;
7 |
8 | public class ZipUtils {
9 |
10 | private static final int buffer = 2048;
11 |
12 | /**
13 | * 解压Zip文件
14 | *
15 | * @param path 文件目录
16 | */
17 | public static String unZip(String path) {
18 | int count = -1;
19 | String savepath = null;
20 |
21 | File file = null;
22 | InputStream is = null;
23 | FileOutputStream fos = null;
24 | BufferedOutputStream bos = null;
25 |
26 | savepath = path.substring(0, path.lastIndexOf(".")) + File.separator; //保存解压文件目录
27 | File dir = new File(savepath);
28 | if (dir.exists()) {
29 | BaseUtils.deleteDir(dir);
30 | }
31 | dir.mkdir(); //创建保存目录
32 |
33 | ZipFile zipFile = null;
34 | try {
35 | zipFile = new ZipFile(path); //解决中文乱码问题
36 | Enumeration> entries = zipFile.entries();
37 |
38 | while (entries.hasMoreElements()) {
39 | byte buf[] = new byte[buffer];
40 |
41 | ZipEntry entry = (ZipEntry) entries.nextElement();
42 |
43 | String filename = entry.getName();
44 | boolean ismkdir = false;
45 | if (filename.lastIndexOf("/") != -1) { //检查此文件是否带有文件夹
46 | ismkdir = true;
47 | }
48 | filename = savepath + filename;
49 |
50 | if (entry.isDirectory()) { //如果是文件夹先创建
51 | file = new File(filename);
52 | file.mkdirs();
53 | continue;
54 | }
55 | file = new File(filename);
56 | if (!file.exists()) { //如果是目录先创建
57 | if (ismkdir) {
58 | new File(filename.substring(0, filename.lastIndexOf("/"))).mkdirs(); //目录先创建
59 | }
60 | }
61 | file.createNewFile(); //创建文件
62 |
63 | is = zipFile.getInputStream(entry);
64 | fos = new FileOutputStream(file);
65 | bos = new BufferedOutputStream(fos, buffer);
66 |
67 | while ((count = is.read(buf)) > -1) {
68 | bos.write(buf, 0, count);
69 | }
70 | bos.flush();
71 | bos.close();
72 | fos.close();
73 |
74 | is.close();
75 | }
76 |
77 | zipFile.close();
78 |
79 | } catch (IOException ioe) {
80 | ioe.printStackTrace();
81 | } finally {
82 | try {
83 | if (bos != null) {
84 | bos.close();
85 | }
86 | if (fos != null) {
87 | fos.close();
88 | }
89 | if (is != null) {
90 | is.close();
91 | }
92 | if (zipFile != null) {
93 | zipFile.close();
94 | }
95 | } catch (Exception e) {
96 | e.printStackTrace();
97 | }
98 | }
99 |
100 | return savepath;
101 | }
102 |
103 |
104 | public static String unZip2CurrentDir(String path) {
105 | int count = -1;
106 | String savepath = null;
107 |
108 | File file = null;
109 | InputStream is = null;
110 | FileOutputStream fos = null;
111 | BufferedOutputStream bos = null;
112 |
113 | savepath = new File(path).getParentFile().getPath();
114 |
115 | ZipFile zipFile = null;
116 | try {
117 | zipFile = new ZipFile(path); //解决中文乱码问题
118 | Enumeration> entries = zipFile.entries();
119 |
120 | while (entries.hasMoreElements()) {
121 | byte buf[] = new byte[buffer];
122 |
123 | ZipEntry entry = (ZipEntry) entries.nextElement();
124 |
125 | String filename = entry.getName();
126 | boolean ismkdir = false;
127 | if (filename.lastIndexOf(File.separator) != -1) { //检查此文件是否带有文件夹
128 | ismkdir = true;
129 | }
130 | filename = savepath +File.separator+ filename;
131 |
132 | if (entry.isDirectory()) { //如果是文件夹先创建
133 | file = new File(filename);
134 | file.mkdirs();
135 | continue;
136 | }
137 | file = new File(filename);
138 | if (!file.exists()) { //如果是目录先创建
139 | if (ismkdir) {
140 | new File(filename.substring(0, filename.lastIndexOf(File.separator))).mkdirs(); //目录先创建
141 | }
142 | }
143 | file.createNewFile(); //创建文件
144 |
145 | is = zipFile.getInputStream(entry);
146 | fos = new FileOutputStream(file);
147 | bos = new BufferedOutputStream(fos, buffer);
148 |
149 | while ((count = is.read(buf)) > -1) {
150 | bos.write(buf, 0, count);
151 | }
152 | bos.flush();
153 | bos.close();
154 | fos.close();
155 |
156 | is.close();
157 | }
158 |
159 | zipFile.close();
160 |
161 | } catch (IOException ioe) {
162 | ioe.printStackTrace();
163 | } finally {
164 | try {
165 | if (bos != null) {
166 | bos.close();
167 | }
168 | if (fos != null) {
169 | fos.close();
170 | }
171 | if (is != null) {
172 | is.close();
173 | }
174 | if (zipFile != null) {
175 | zipFile.close();
176 | }
177 | } catch (Exception e) {
178 | e.printStackTrace();
179 | }
180 | }
181 |
182 | return savepath;
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/main/resources/apktool-2.4.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadisting/ApkDex2jar/a23742108915a4661a42d64d703998954aee7a25/src/main/resources/apktool-2.4.1.zip
--------------------------------------------------------------------------------
/src/main/resources/dex2jar-2.0.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadisting/ApkDex2jar/a23742108915a4661a42d64d703998954aee7a25/src/main/resources/dex2jar-2.0.zip
--------------------------------------------------------------------------------
/src/main/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadisting/ApkDex2jar/a23742108915a4661a42d64d703998954aee7a25/src/main/resources/icon.png
--------------------------------------------------------------------------------
/src/main/resources/jd-gui-1.6.5.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kadisting/ApkDex2jar/a23742108915a4661a42d64d703998954aee7a25/src/main/resources/jd-gui-1.6.5.jar
--------------------------------------------------------------------------------