├── .DS_Store ├── .gitignore ├── README.md ├── config ├── charles.cer ├── config.properties └── test.jks ├── img ├── charles1.png ├── charles2.png ├── charles3.png ├── fiddler1.png ├── fiddler2.png ├── fiddler3.png ├── mac1.png ├── mac2.png ├── mac3.png ├── win1.png ├── win2.png └── win3.png ├── lib └── apktool-fixed.jar ├── src ├── .DS_Store ├── ApkConfig.java ├── ApkCrack.java ├── ApkProcessDialog.java ├── ConfigDialog.java ├── ConfigUtil.java ├── META-INF │ └── MANIFEST.MF ├── Main.java ├── MainUI.java ├── ProgressUtil.java └── StringUtil.java └── tnews-out.apk /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | build 3 | config/*.apk 4 | out/ 5 | *.iml 6 | config/tmpBuild/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## A tool that make your apk debuggable for Charles/Fiddler with Android 6.0 2 | 3 | ## requirement 4 | - jdk 5 | - make sure `jarsigner` works 6 | - Fiddler/Charles 7 | 8 | ## How to use 9 | we can use it in GUI,command line,library 10 | download latest [release](https://github.com/iamyours/ApkCrack/releases) version. 11 | 12 | ### GUI 13 | ```sh 14 | java -jar ApkCrackUI.jar 15 | ``` 16 | 17 | ### command line use 18 | config file in `config/config.properties` 19 | - add apk path,out apk path 20 | - add your sign file(or you can use test.jks) 21 | - add your certFile that export from Charles/Fiddler。 22 | 23 | ``` sh 24 | java -jar ApkCrack.jar 25 | ``` 26 | 27 | ### library 28 | add ApkCrack.jar in your project. 29 | ``` java 30 | apkCrack.setApkFile(apkFile); 31 | apkCrack.setCertFile(certFile); 32 | apkCrack.setStoreFile(storeFile); 33 | apkCrack.setStorePassword(storePassword); 34 | apkCrack.setKeyAlias(keyAlias); 35 | apkCrack.setKeyPassword(keyPassword); 36 | apkCrack.start(); 37 | ``` 38 | 39 | ## export cert file from Charles 40 | open Charles,then `Help` -> `SSL Proxying` -> `Save Charles Root Certificate...` -> `change Format to Binary certificate(.cer)`-> `Save` 41 | ![](img/charles1.png) 42 | 43 | ![](img/charles2.png) 44 | ## export cert file from Fiddler 45 | ![](img/fiddler1.png) 46 | 47 | ![](img/fiddler2.png) 48 | 49 | ## ApkCrack for mac 50 | ![](img/mac1.png) 51 | 52 | ![](img/mac2.png) 53 | 54 | ![](img/mac3.png) 55 | 56 | ![](img/charles3.png) 57 | 58 | ## ApkCrack for windows 59 | ![](img/win1.png) 60 | 61 | ![](img/win2.png) 62 | 63 | ![](img/fiddler3.png) 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /config/charles.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/config/charles.cer -------------------------------------------------------------------------------- /config/config.properties: -------------------------------------------------------------------------------- 1 | #if true: add debuggable to application in AndroidManifest.xml 2 | debuggable=true 3 | #if true: add networkSecurityConfig to application in AndroidManifest.xml 4 | #if config already, just change network_security_config.xml 5 | networkSecurityConfig=true 6 | # certificate file that export from Charles/Fiddler 7 | certFile=config/charles.cer 8 | # sign config for apk 9 | storeFile=config/test.jks 10 | storePassword=qqqqqq 11 | keyAlias=test 12 | keyPassword=qqqqqq 13 | 14 | # apk path 15 | apkFile=/Users/xxx/Downloads/tianmao.apk 16 | outFile=/Users/xxx/Downloads/tianmao-out.apk 17 | -------------------------------------------------------------------------------- /config/test.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/config/test.jks -------------------------------------------------------------------------------- /img/charles1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/charles1.png -------------------------------------------------------------------------------- /img/charles2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/charles2.png -------------------------------------------------------------------------------- /img/charles3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/charles3.png -------------------------------------------------------------------------------- /img/fiddler1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/fiddler1.png -------------------------------------------------------------------------------- /img/fiddler2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/fiddler2.png -------------------------------------------------------------------------------- /img/fiddler3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/fiddler3.png -------------------------------------------------------------------------------- /img/mac1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/mac1.png -------------------------------------------------------------------------------- /img/mac2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/mac2.png -------------------------------------------------------------------------------- /img/mac3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/mac3.png -------------------------------------------------------------------------------- /img/win1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/win1.png -------------------------------------------------------------------------------- /img/win2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/win2.png -------------------------------------------------------------------------------- /img/win3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/img/win3.png -------------------------------------------------------------------------------- /lib/apktool-fixed.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/lib/apktool-fixed.jar -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/src/.DS_Store -------------------------------------------------------------------------------- /src/ApkConfig.java: -------------------------------------------------------------------------------- 1 | import org.apache.commons.lang3.StringUtils; 2 | 3 | public class ApkConfig { 4 | public String storeFile; 5 | public String storePassword; 6 | public String keyAlias; 7 | public String keyPassword; 8 | public String certFile; 9 | 10 | public boolean isInvalid() { 11 | return StringUtils.isEmpty(storeFile) || StringUtils.isEmpty(storePassword) || StringUtils.isEmpty(keyAlias) || StringUtils.isEmpty(keyPassword) || StringUtils.isEmpty(certFile); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ApkCrack.java: -------------------------------------------------------------------------------- 1 | import brut.androlib.Androlib; 2 | import brut.androlib.ApkDecoder; 3 | import brut.androlib.ApkOptions; 4 | import brut.common.BrutException; 5 | import org.apache.commons.io.FileUtils; 6 | import org.w3c.dom.*; 7 | 8 | import javax.xml.parsers.DocumentBuilder; 9 | import javax.xml.parsers.DocumentBuilderFactory; 10 | import javax.xml.transform.Transformer; 11 | import javax.xml.transform.TransformerFactory; 12 | import javax.xml.transform.dom.DOMSource; 13 | import javax.xml.transform.stream.StreamResult; 14 | import java.io.*; 15 | import java.util.Properties; 16 | import java.util.logging.Logger; 17 | 18 | public class ApkCrack { 19 | private boolean debuggable; 20 | private boolean networkSecurityConfig; 21 | private String certFile; 22 | private String certName; 23 | private String certFileName; 24 | private String storeFile; 25 | private String storePassword; 26 | private String keyAlias; 27 | private String keyPassword; 28 | private String apkFile; 29 | private String outFile; 30 | 31 | private String buildPath; 32 | 33 | private static final String ANDROID_MANIFEST_PATH = "AndroidManifest.xml"; 34 | private static final String ATTR_NETCONFIG = "android:networkSecurityConfig"; 35 | private static final String ATTR_DEBUG = "android:debuggable"; 36 | private static final String ATTR_CERT = "certificates"; 37 | private static final String ATTR_TRUST = "trust-anchors"; 38 | private long s; 39 | private static final Logger LOG = Logger.getLogger(ApkCrack.class.getName()); 40 | 41 | public void setCertFile(String certFile) { 42 | this.certFile = certFile; 43 | } 44 | 45 | public void setStoreFile(String storeFile) { 46 | this.storeFile = storeFile; 47 | } 48 | 49 | public void setStorePassword(String storePassword) { 50 | this.storePassword = storePassword; 51 | } 52 | 53 | public void setKeyAlias(String keyAlias) { 54 | this.keyAlias = keyAlias; 55 | } 56 | 57 | public void setKeyPassword(String keyPassword) { 58 | this.keyPassword = keyPassword; 59 | } 60 | 61 | public void setApkFile(String apkFile) { 62 | this.apkFile = apkFile; 63 | } 64 | 65 | public String getOutFile() { 66 | return outFile; 67 | } 68 | 69 | public void setDebuggable(boolean debuggable) { 70 | this.debuggable = debuggable; 71 | } 72 | 73 | public void start() throws Exception { 74 | s = System.currentTimeMillis(); 75 | if (storeFile != null) { 76 | networkSecurityConfig = true; 77 | 78 | setNetWork(); 79 | setOutFile(); 80 | } else { 81 | initConfig(); 82 | } 83 | setBuildPath(); 84 | decodeApk(); 85 | hook(); 86 | 87 | generateApk(); 88 | signApk(); 89 | LOG.info("done..."); 90 | } 91 | 92 | private void setBuildPath() { 93 | File file = new File(apkFile); 94 | buildPath = new File(file.getParent(), "tmpBuild").getAbsolutePath(); 95 | } 96 | 97 | private void setOutFile() { 98 | File file = new File(apkFile); 99 | String parent = file.getParent(); 100 | String fileName = file.getName(); 101 | outFile = new File(parent, "out-" + fileName).getAbsolutePath(); 102 | } 103 | 104 | private void signApk() { 105 | String cmd = "jarsigner -verbose -keystore " + storeFile + " -storepass " + storePassword + " -keypass " + keyPassword + " " + outFile + " " + keyAlias; 106 | LOG.info("start sign apk..."); 107 | try { 108 | Process p = Runtime.getRuntime().exec(cmd); 109 | InputStream is = p.getInputStream(); 110 | BufferedReader br = new BufferedReader(new InputStreamReader(is)); 111 | while (true) { 112 | String s = br.readLine(); 113 | if (s == null) 114 | break; 115 | LOG.info(s); 116 | } 117 | br.readLine(); 118 | } catch (IOException e) { 119 | e.printStackTrace(); 120 | } 121 | LOG.info(">>>> finished....." + (System.currentTimeMillis() - s) + "ms"); 122 | } 123 | 124 | //generate apk with apktool 125 | private void generateApk() throws Exception { 126 | LOG.info("start build unsigned apk..."); 127 | ApkOptions opt = new ApkOptions(); 128 | new Androlib(opt).build(new File(buildPath), new File(outFile)); 129 | 130 | } 131 | 132 | //add debuggable ,networkSecurityConfig 133 | private void hook() throws Exception { 134 | LOG.info("start hook apk..."); 135 | LOG.info(">>>>>parsing AndroidManifest.xml....."); 136 | File file = new File(buildPath, ANDROID_MANIFEST_PATH); 137 | DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); 138 | DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); 139 | Document document = documentBuilder.parse(file); 140 | Node application = document.getElementsByTagName("application").item(0); 141 | 142 | NamedNodeMap namedNodeMap = application.getAttributes(); 143 | if (networkSecurityConfig) { 144 | addCertFile(); 145 | LOG.info(">>>>>adding networkSecurityConfig attribute..."); 146 | Attr attr = document.createAttribute(ATTR_NETCONFIG); 147 | attr.setValue("@xml/network_security_config"); 148 | namedNodeMap.setNamedItem(attr); 149 | File xmlPath = new File("build/res/xml/"); 150 | if (!xmlPath.exists()) { 151 | LOG.info(">>>>> mkdirs xml path..."); 152 | xmlPath.mkdirs(); 153 | } 154 | addNetConfig(buildPath + "/res/xml/network_security_config.xml"); 155 | } 156 | 157 | if (debuggable) { 158 | LOG.info(">>>>>adding debuggable attribute..."); 159 | Attr attr = document.createAttribute(ATTR_DEBUG); 160 | attr.setValue("true"); 161 | namedNodeMap.setNamedItem(attr); 162 | } 163 | if (debuggable || networkSecurityConfig) { 164 | TransformerFactory transformerFactory = TransformerFactory.newInstance(); 165 | Transformer transformer = transformerFactory.newTransformer(); 166 | DOMSource domSource = new DOMSource(document); 167 | StreamResult reStreamResult = new StreamResult(file); 168 | transformer.transform(domSource, reStreamResult); 169 | File tmp = new File(buildPath, ANDROID_MANIFEST_PATH + ".tmp"); 170 | StreamResult consoleResult = new StreamResult(tmp); 171 | transformer.transform(domSource, consoleResult); 172 | if (file.exists()) file.delete(); 173 | tmp.renameTo(file); 174 | } 175 | } 176 | 177 | private void addNetConfig(String outFile) throws Exception { 178 | LOG.info(">>>adding certificate to " + outFile); 179 | DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); 180 | DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); 181 | Document document = documentBuilder.parse(StringUtil.netConfigStream()); 182 | Node node = document.getElementsByTagName(ATTR_TRUST).item(0); 183 | Element element = document.createElement(ATTR_CERT); 184 | element.setAttribute("src", "@raw/" + certName); 185 | node.appendChild(element); 186 | TransformerFactory transformerFactory = TransformerFactory.newInstance(); 187 | Transformer transformer = transformerFactory.newTransformer(); 188 | DOMSource domSource = new DOMSource(document); 189 | File tmp = new File(buildPath + "netConfig.tmp"); 190 | StreamResult consoleResult = new StreamResult(tmp); 191 | transformer.transform(domSource, consoleResult); 192 | File out = new File(outFile); 193 | File parent = out.getParentFile(); 194 | if (!parent.exists()) parent.mkdirs(); 195 | if (out.exists()) out.delete(); 196 | tmp.renameTo(out); 197 | } 198 | 199 | private void addCertFile() { 200 | try { 201 | FileUtils.copyFile(new File(certFile), new File(buildPath + "/res/raw/", certFileName)); 202 | } catch (IOException e) { 203 | throw new RuntimeException(e); 204 | } 205 | } 206 | 207 | private void initConfig() { 208 | LOG.info("init config..."); 209 | Properties p = new Properties(); 210 | try { 211 | p.load(new FileInputStream(new File("config/config.properties"))); 212 | debuggable = Boolean.parseBoolean(p.getProperty("debuggable")); 213 | networkSecurityConfig = Boolean.parseBoolean(p.getProperty("networkSecurityConfig")); 214 | certFile = p.getProperty("certFile"); 215 | storeFile = p.getProperty("storeFile"); 216 | storePassword = p.getProperty("storePassword"); 217 | keyAlias = p.getProperty("keyAlias"); 218 | keyPassword = p.getProperty("keyPassword"); 219 | apkFile = p.getProperty("apkFile"); 220 | outFile = p.getProperty("outFile"); 221 | 222 | } catch (IOException e) { 223 | e.printStackTrace(); 224 | } 225 | LOG.info(configValue()); 226 | setNetWork(); 227 | if (!new File(storeFile).exists()) { 228 | LOG.info("error: storeFile not exist"); 229 | } 230 | if (!new File(apkFile).exists()) { 231 | LOG.info("error: apkFile not exist"); 232 | } 233 | } 234 | 235 | private void setNetWork() { 236 | if (networkSecurityConfig) { 237 | File c = new File(certFile); 238 | certFileName = c.getName(); 239 | certName = certFileName.substring(0, certFileName.lastIndexOf(".")); 240 | if (!c.exists()) { 241 | LOG.info(certName); 242 | LOG.info("error: certFile not exist"); 243 | } 244 | 245 | } 246 | } 247 | 248 | private void decodeApk() { 249 | File build = new File(buildPath); 250 | if (build.exists()) { 251 | LOG.info("remove build....."); 252 | deleteDir(build); 253 | } 254 | LOG.info("start decode apk..."); 255 | LOG.info(">>>>decode apk...."); 256 | ApkDecoder decoder = new ApkDecoder(); 257 | try { 258 | decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_NONE); 259 | decoder.setApkFile(new File(apkFile)); 260 | decoder.setOutDir(build); 261 | decoder.decode(); 262 | } catch (Exception e) { 263 | throw new RuntimeException(e); 264 | } 265 | } 266 | 267 | 268 | private static boolean deleteDir(File dir) { 269 | if (dir.isDirectory()) { 270 | String[] children = dir.list(); 271 | for (int i = 0; i < children.length; i++) { 272 | boolean success = deleteDir(new File(dir, children[i])); 273 | if (!success) { 274 | return false; 275 | } 276 | } 277 | } 278 | return dir.delete(); 279 | } 280 | 281 | private String configValue() { 282 | return ">>>>>config {" + 283 | "debuggable=" + debuggable + 284 | ", networkSecurityConfig=" + networkSecurityConfig + 285 | ", certFile='" + certFile + '\'' + 286 | ", storeFile='" + storeFile + '\'' + 287 | ", storePassword='" + storePassword + '\'' + 288 | ", keyAlias='" + keyAlias + '\'' + 289 | ", keyPassword='" + keyPassword + '\'' + 290 | ", apkFile='" + apkFile + '\'' + 291 | ", outFile='" + outFile + '\'' + 292 | '}'; 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /src/ApkProcessDialog.java: -------------------------------------------------------------------------------- 1 | import javax.swing.*; 2 | import javax.swing.text.*; 3 | import java.awt.*; 4 | import java.awt.event.WindowAdapter; 5 | import java.awt.event.WindowEvent; 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | public class ApkProcessDialog extends JDialog { 10 | private String apkFile; 11 | private JLabel mainLabel; 12 | private JTextArea detailLabel; 13 | private JProgressBar progressBar; 14 | private JScrollPane scrollPane; 15 | private Document document; 16 | 17 | public void setApkFile(String apkFile) { 18 | this.apkFile = apkFile; 19 | } 20 | 21 | public ApkProcessDialog(Frame owner) { 22 | super(owner); 23 | JPanel panel = new JPanel(); 24 | setTitle("Processing Apk..."); 25 | panel.setLayout(new BorderLayout()); 26 | setSize(400, 280); 27 | mainLabel = new JLabel(""); 28 | progressBar = new JProgressBar(); 29 | progressBar.setMaximum(100); 30 | JPanel top = new JPanel(); 31 | top.setLayout(new GridLayout(2, 1)); 32 | top.add(mainLabel); 33 | top.add(progressBar); 34 | scrollPane = new JScrollPane(); 35 | scrollPane.setSize(400, 300); 36 | detailLabel = new JTextArea(""); 37 | detailLabel.setLineWrap(true); 38 | detailLabel.setWrapStyleWord(true); 39 | document = new DefaultStyledDocument(); 40 | detailLabel.setDocument(document); 41 | scrollPane.setViewportView(detailLabel); 42 | panel.add(top, BorderLayout.NORTH); 43 | panel.add(scrollPane, BorderLayout.CENTER); 44 | setContentPane(panel); 45 | setLocationRelativeTo(getOwner()); 46 | addWindowListener(new WindowAdapter() { 47 | @Override 48 | public void windowClosing(WindowEvent e) { 49 | super.windowClosing(e); 50 | System.out.println("closing..."); 51 | if (processThread != null) { 52 | processThread.stop(); 53 | } 54 | } 55 | }); 56 | } 57 | 58 | private Thread processThread; 59 | ApkCrack apkCrack; 60 | 61 | public void start() { 62 | apkCrack = new ApkCrack(); 63 | ApkConfig config = ConfigUtil.loadConfig(); 64 | apkCrack.setApkFile(apkFile); 65 | apkCrack.setCertFile(config.certFile); 66 | apkCrack.setStoreFile(config.storeFile); 67 | apkCrack.setStorePassword(config.storePassword); 68 | apkCrack.setKeyAlias(config.keyAlias); 69 | apkCrack.setKeyPassword(config.keyPassword); 70 | apkCrack.setDebuggable(true); 71 | setVisible(true); 72 | processThread = new Thread(() -> { 73 | ProgressUtil.init(); 74 | setProgressHandler(); 75 | try { 76 | apkCrack.start(); 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | showErrorDialog(e); 80 | } 81 | }); 82 | try { 83 | Thread.sleep(100); 84 | } catch (InterruptedException e) { 85 | e.printStackTrace(); 86 | } 87 | processThread.start(); 88 | } 89 | 90 | private void showErrorDialog(Exception e) { 91 | SimpleAttributeSet attrs = new SimpleAttributeSet(); 92 | try { 93 | document.insertString(document.getLength(), e.getMessage() + "\n", attrs); 94 | } catch (BadLocationException e1) { 95 | e1.printStackTrace(); 96 | } 97 | mainLabel.setText("Process failed!"); 98 | } 99 | 100 | private StringBuilder sb = new StringBuilder(); 101 | 102 | private void setProgressHandler() { 103 | ProgressUtil.setMsgHandler(new ProgressUtil.ProgressHandler() { 104 | @Override 105 | public void progress(String msg1, String msg2, float progress) { 106 | 107 | SwingUtilities.invokeLater(new Runnable() { 108 | @Override 109 | public void run() { 110 | if (msg1 != null) { 111 | SimpleAttributeSet attrs = new SimpleAttributeSet(); 112 | try { 113 | document.insertString(document.getLength(), msg1 + "\n", attrs); 114 | } catch (BadLocationException e) { 115 | e.printStackTrace(); 116 | } 117 | } 118 | if (msg2 != null) { 119 | mainLabel.setText(msg2); 120 | } 121 | if (progress > 0) { 122 | progressBar.setValue((int) (progress * 100)); 123 | } 124 | if ("done...".equals(msg1)) { 125 | showDoneDialog(); 126 | } 127 | 128 | } 129 | 130 | }); 131 | 132 | } 133 | }); 134 | } 135 | 136 | private void showDoneDialog() { 137 | int result = JOptionPane.showConfirmDialog(this, "Process finished!Open file in directory?", "Message", JOptionPane.YES_NO_CANCEL_OPTION); 138 | if (result == 0) { 139 | String outFile = apkCrack.getOutFile(); 140 | File file = new File(outFile); 141 | try { 142 | Desktop.getDesktop().open(file.getParentFile()); 143 | } catch (IOException e) { 144 | e.printStackTrace(); 145 | } 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/ConfigDialog.java: -------------------------------------------------------------------------------- 1 | import javax.swing.*; 2 | import java.awt.*; 3 | 4 | public class ConfigDialog extends JDialog { 5 | private JTextField storeFileText; 6 | private JTextField storePasswordText; 7 | private JTextField keyAliasText; 8 | private JTextField keyPasswordText; 9 | private JTextField certFileText; 10 | 11 | public ConfigDialog(Frame owner) { 12 | super(owner); 13 | init(); 14 | } 15 | 16 | private void init() { 17 | Box vBox = Box.createVerticalBox(); 18 | setSize(400, 300); 19 | JPanel panel01 = new JPanel(new FlowLayout(FlowLayout.LEFT)); 20 | panel01.add(new JLabel("StoreFile")); 21 | setTitle("Config"); 22 | storeFileText = new JTextField(20); 23 | panel01.add(storeFileText); 24 | JButton storeBtn = new JButton("Open"); 25 | panel01.add(storeBtn); 26 | storeBtn.addActionListener(e -> { 27 | storeFileText.setText(openFile()); 28 | }); 29 | JPanel panel2 = new JPanel(new FlowLayout(FlowLayout.LEFT)); 30 | panel2.add(new JLabel("StorePassword")); 31 | storePasswordText = new JPasswordField(10); 32 | panel2.add(storePasswordText); 33 | 34 | JPanel panel3 = new JPanel(new FlowLayout(FlowLayout.LEFT)); 35 | panel3.add(new JLabel("KeyAlias")); 36 | keyAliasText = new JTextField(15); 37 | panel3.add(keyAliasText); 38 | 39 | JPanel panel4 = new JPanel(new FlowLayout(FlowLayout.LEFT)); 40 | panel4.add(new JLabel("KeyPassword")); 41 | keyPasswordText = new JPasswordField(15); 42 | panel4.add(keyPasswordText); 43 | 44 | JPanel panel5 = new JPanel(new FlowLayout(FlowLayout.LEFT)); 45 | panel5.add(new JLabel("CertFile")); 46 | certFileText = new JTextField(20); 47 | panel5.add(certFileText); 48 | JButton certBtn = new JButton("Open"); 49 | certBtn.addActionListener(e -> { 50 | certFileText.setText(openFile()); 51 | }); 52 | panel5.add(certBtn); 53 | 54 | JPanel panel6 = new JPanel(new FlowLayout(FlowLayout.RIGHT)); 55 | JButton configBtn = new JButton("Ok"); 56 | configBtn.addActionListener(e -> saveConfig()); 57 | panel6.add(configBtn); 58 | 59 | vBox.add(panel01); 60 | vBox.add(panel2); 61 | vBox.add(panel3); 62 | vBox.add(panel4); 63 | vBox.add(panel5); 64 | vBox.add(panel6); 65 | loadConfig(); 66 | setContentPane(vBox); 67 | setLocationRelativeTo(getOwner()); 68 | } 69 | 70 | private void loadConfig() { 71 | ApkConfig config = ConfigUtil.loadConfig(); 72 | if (config == null) return; 73 | certFileText.setText(config.certFile); 74 | storeFileText.setText(config.storeFile); 75 | storePasswordText.setText(config.storePassword); 76 | keyAliasText.setText(config.keyAlias); 77 | keyPasswordText.setText(config.keyPassword); 78 | } 79 | 80 | private void saveConfig() { 81 | ApkConfig config = new ApkConfig(); 82 | config.storeFile = storeFileText.getText(); 83 | config.storePassword = storePasswordText.getText(); 84 | config.keyAlias = keyAliasText.getText(); 85 | config.keyPassword = keyPasswordText.getText(); 86 | config.certFile = certFileText.getText(); 87 | ConfigUtil.saveConfig(config); 88 | setVisible(false); 89 | } 90 | 91 | private String openFile() { 92 | FileDialog dialog = new FileDialog(new Frame(), "File"); 93 | dialog.setVisible(true); 94 | return dialog.getDirectory() + dialog.getFile(); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/ConfigUtil.java: -------------------------------------------------------------------------------- 1 | import java.io.*; 2 | import java.util.Properties; 3 | 4 | public class ConfigUtil { 5 | private static final String CONFIG_FILE = "ApkCrack.config.properties"; 6 | 7 | public static ApkConfig loadConfig() { 8 | File file = new File(CONFIG_FILE); 9 | if (file.exists()) { 10 | return createConfig(file); 11 | } else { 12 | return null; 13 | } 14 | } 15 | 16 | public static void saveConfig(ApkConfig config) { 17 | Properties p = new Properties(); 18 | p.setProperty("certFile", config.certFile); 19 | p.setProperty("storeFile", config.storeFile); 20 | p.setProperty("storePassword", config.storePassword); 21 | p.setProperty("keyAlias", config.keyAlias); 22 | p.setProperty("keyPassword", config.keyPassword); 23 | try { 24 | FileOutputStream fos = new FileOutputStream(new File(CONFIG_FILE)); 25 | p.store(fos, ""); 26 | } catch (FileNotFoundException e) { 27 | e.printStackTrace(); 28 | } catch (IOException e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | 33 | private static ApkConfig createConfig(File file) { 34 | Properties p = new Properties(); 35 | ApkConfig config = new ApkConfig(); 36 | try { 37 | p.load(new FileInputStream(file)); 38 | config.certFile = p.getProperty("certFile"); 39 | config.storeFile = p.getProperty("storeFile"); 40 | config.storePassword = p.getProperty("storePassword"); 41 | config.keyAlias = p.getProperty("keyAlias"); 42 | config.keyPassword = p.getProperty("keyPassword"); 43 | } catch (IOException e) { 44 | e.printStackTrace(); 45 | } 46 | return config; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: Main 3 | 4 | -------------------------------------------------------------------------------- /src/Main.java: -------------------------------------------------------------------------------- 1 | public class Main { 2 | 3 | public static void main(String[] args) { 4 | ProgressUtil.init(); 5 | try { 6 | new ApkCrack().start(); 7 | } catch (Exception e) { 8 | e.printStackTrace(); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/MainUI.java: -------------------------------------------------------------------------------- 1 | import javax.swing.*; 2 | import java.awt.*; 3 | import java.awt.event.ActionEvent; 4 | import java.awt.event.ActionListener; 5 | import java.io.File; 6 | 7 | public class MainUI { 8 | public static void main(String[] args) { 9 | openMainPanel(); 10 | } 11 | 12 | private static JTextField apkFileText; 13 | 14 | private static void openMainPanel() { 15 | JFrame jf = new JFrame("ApkCrack"); 16 | jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 17 | 18 | // 第 1 个 JPanel, 使用默认的浮动布局 19 | JPanel panel01 = new JPanel(); 20 | panel01.add(new JLabel("ApkFile")); 21 | apkFileText = new JTextField(20); 22 | panel01.add(apkFileText); 23 | JButton fileBtn = new JButton("Open"); 24 | fileBtn.addActionListener(e -> apkFileText.setText(openFile())); 25 | panel01.add(fileBtn); 26 | 27 | 28 | // 第 3 个 JPanel, 使用浮动布局, 并且容器内组件居中显示 29 | JPanel panel03 = new JPanel(new FlowLayout(FlowLayout.RIGHT)); 30 | JButton configBtn = new JButton("Config"); 31 | configBtn.addActionListener(new ActionListener() { 32 | @Override 33 | public void actionPerformed(ActionEvent e) { 34 | openConfigDialog(jf); 35 | } 36 | }); 37 | panel03.add(configBtn); 38 | JButton goBtn = new JButton("GO"); 39 | goBtn.addActionListener(e -> { 40 | processApk(jf); 41 | }); 42 | panel03.add(goBtn); 43 | 44 | Box vBox = Box.createVerticalBox(); 45 | vBox.add(panel01); 46 | vBox.add(panel03); 47 | 48 | jf.setContentPane(vBox); 49 | 50 | jf.pack(); 51 | jf.setLocationRelativeTo(null); 52 | jf.setVisible(true); 53 | } 54 | 55 | private static String openFile() { 56 | FileDialog dialog = new FileDialog(new Frame(), "File"); 57 | dialog.setVisible(true); 58 | return dialog.getDirectory() + dialog.getFile(); 59 | } 60 | 61 | private static void processApk(JFrame jf) { 62 | ApkConfig config = ConfigUtil.loadConfig(); 63 | if (config == null || config.isInvalid()) { 64 | int result = JOptionPane.showConfirmDialog(jf, "Please config Store File and Cert File!", "Message", JOptionPane.OK_OPTION); 65 | if (result == 0) { 66 | openConfigDialog(jf); 67 | } 68 | return; 69 | } 70 | String apkFile = apkFileText.getText(); 71 | 72 | if (apkFile == null || "".equals(apkFile) || !new File(apkFile).exists()) { 73 | int result = JOptionPane.showConfirmDialog(jf, "Please select apk file", "Message", JOptionPane.OK_OPTION); 74 | if (result == 0) { 75 | apkFileText.setText(openFile()); 76 | } 77 | return; 78 | } 79 | 80 | ApkProcessDialog dialog = new ApkProcessDialog(jf); 81 | dialog.setApkFile(apkFile); 82 | dialog.start(); 83 | } 84 | 85 | private static void openConfigDialog(JFrame jf) { 86 | ConfigDialog dialog = new ConfigDialog(jf); 87 | dialog.setVisible(true); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/ProgressUtil.java: -------------------------------------------------------------------------------- 1 | import brut.androlib.Androlib; 2 | import brut.androlib.res.AndrolibResources; 3 | 4 | import java.lang.reflect.Field; 5 | import java.util.concurrent.CopyOnWriteArrayList; 6 | import java.util.concurrent.Executor; 7 | import java.util.concurrent.Executors; 8 | import java.util.logging.Handler; 9 | import java.util.logging.LogRecord; 10 | import java.util.logging.Logger; 11 | 12 | public class ProgressUtil { 13 | private static String[] detailMsgs = new String[]{ 14 | "remove build.....", 15 | //decode 16 | "Loading resource table...", 17 | "Decoding AndroidManifest.xml with resources..", 18 | "Loading resource table from file: ", 19 | "Regular manifest package...", 20 | "Decoding values */* XMLs...", 21 | "Copying raw classes", 22 | "Copying assets and libs...", 23 | "Copying unknown files...", 24 | "Copying original files...", 25 | //hook 26 | "start hook apk...", 27 | 28 | //build 29 | "Building resources...", 30 | "Copying libs... (/lib)", 31 | "Building apk file...", 32 | "Copying unknown files/dir...", 33 | "Built apk...", 34 | //sign 35 | "start sign apk...", 36 | "done..." 37 | 38 | }; 39 | private static String[] msgs = new String[]{ 40 | "init config...", 41 | "start decode apk...", 42 | "remove build.....", 43 | "start hook apk...", 44 | "start build unsigned apk...", 45 | "start sign apk...", 46 | "done..." 47 | }; 48 | private static Class[] LoggerClasses = new Class[]{ 49 | AndrolibResources.class, 50 | Androlib.class, 51 | ApkCrack.class 52 | }; 53 | 54 | public interface ProgressHandler { 55 | void progress(String msg1, String msg2, float progress); 56 | } 57 | 58 | private static ProgressHandler progressHandler; 59 | 60 | private static ProgressHandler getHandler() { 61 | return progressHandler; 62 | } 63 | 64 | private static Executor executor = Executors.newSingleThreadExecutor(); 65 | 66 | 67 | public static void init() { 68 | for (Class cls : LoggerClasses) { 69 | Logger logger = Logger.getLogger(cls.getName()); 70 | clearHandler(logger); 71 | logger.addHandler(new Handler() { 72 | @Override 73 | public void publish(LogRecord record) { 74 | System.out.println(record.getMessage()); 75 | executor.execute(new Runnable() { 76 | @Override 77 | public void run() { 78 | if (getHandler() == null) return; 79 | String msg = record.getMessage(); 80 | String msg2 = null; 81 | float progress = 0f; 82 | 83 | for (int i = 0; i < detailMsgs.length; i++) { 84 | if (msg.startsWith(detailMsgs[i])) { 85 | progress = (i + 1f) / detailMsgs.length; 86 | break; 87 | } 88 | } 89 | for (String m : msgs) { 90 | if (msg.startsWith(m)) { 91 | msg2 = msg; 92 | break; 93 | } 94 | } 95 | getHandler().progress(msg, msg2, progress); 96 | } 97 | }); 98 | } 99 | 100 | @Override 101 | public void flush() { 102 | 103 | } 104 | 105 | @Override 106 | public void close() throws SecurityException { 107 | 108 | } 109 | }); 110 | } 111 | } 112 | 113 | private static void clearHandler(Logger logger) { 114 | try { 115 | Field field = logger.getClass().getDeclaredField("handlers"); 116 | field.setAccessible(true); 117 | CopyOnWriteArrayList list = (CopyOnWriteArrayList) field.get(logger); 118 | list.clear(); 119 | System.out.println("handler size:" + list.size()); 120 | } catch (NoSuchFieldException e) { 121 | e.printStackTrace(); 122 | } catch (IllegalAccessException e) { 123 | e.printStackTrace(); 124 | } 125 | } 126 | 127 | public static void setMsgHandler(ProgressHandler h) { 128 | progressHandler = h; 129 | } 130 | 131 | public static void main(String[] args) { 132 | setMsgHandler(new ProgressHandler() { 133 | @Override 134 | public void progress(String msg1, String msg2, float progress) { 135 | System.out.println(msg1 + "," + msg2 + "," + progress); 136 | } 137 | }); 138 | Logger.getLogger(Androlib.class.getName()).info("test"); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/StringUtil.java: -------------------------------------------------------------------------------- 1 | import java.io.ByteArrayInputStream; 2 | import java.io.InputStream; 3 | 4 | public class StringUtil { 5 | private static final String NET_CONFIG_XML = 6 | "\n" + 7 | "\n" + 8 | " \n" + 9 | " \n" + 10 | " \n" + 11 | " \n" + 12 | " \n" + 13 | " \n" + 14 | ""; 15 | 16 | public static InputStream netConfigStream() { 17 | return new ByteArrayInputStream(NET_CONFIG_XML.getBytes()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tnews-out.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamyours/ApkCrack/10299f25a250eae4625492e8a72ad0936bec0b25/tnews-out.apk --------------------------------------------------------------------------------