diff --git a/.travis.yml b/.travis.yml index e931f86..2bc1e80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ script: - cd apkstudio-latest-release - cp ../apkstudio . - - ../linuxdeployqt*.AppImage apkstudio -bundle-non-qt-libs -no-translations -verbose=2 -appimage + - ../linuxdeployqt*.AppImage apkstudio -bundle-non-qt-libs -no-translations -verbose=2 -appimage -no-copy-copyright-files # removing doc folder, apprun shortcut and .DirIcon # - rm -rf doc AppRun .DirIcon diff --git a/README.md b/README.md index 06adc41..8e2d596 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,15 @@ # APK Studio [![Codacy Badge](https://api.codacy.com/project/badge/Grade/9709de5012824c36b54fda9c2c6390bf)](https://app.codacy.com/app/Surendrajat/apkstudio?utm_source=github.com&utm_medium=referral&utm_content=Surendrajat/apkstudio&utm_campaign=badger) [![Build Status(Linux)](https://travis-ci.org/Surendrajat/ApkStudio.svg)](https://travis-ci.org/Surendrajat/ApkStudio) [![Build status(Windows)](https://ci.appveyor.com/api/projects/status/mnr254lm0mlshmfb?svg=true)](https://ci.appveyor.com/project/Surendrajat/apkstudio) [![Join the chat at https://gitter.im/apk-studio/ideas](https://badges.gitter.im/apk-studio/ideas.svg)](https://gitter.im/apk-studio/ideas?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ->**A Cross-Platform IDE for Reverse-Engineering (Decompiling, Hacking & Rebuilding) Android applications - featuring a friendly UI and Code editor which Syntax-highlighting for `smali` files** +>**A Cross-Platform IDE for Reverse-Engineering (Disassembling, Hacking & Rebuilding) Android applications - featuring a friendly UI and Code editor with Syntax-highlighting support** ### Features -- Decompile & Recompile APKs -- Code Editor with Syntax Highlighting (`smali` `xml` `yml` language) -- Flexible Keystore & Key-Alias Chooser -- Automatic *zipalign* & Apk *sign*ing -- Built-in Image viewer for Resources -- One-click App Install to device +- Disassembling & Rebuilding APKs +- Code Editor with Syntax Highlighting (`java` `smali` `xml` `yml`) +- Built-in Image viewer for Resources +- Single-click APK Signing with embedded key-store *(New)* +- Android Signing scheme v2 Support *(New)* +- Flexible Keystore & Key-Alias import +- Automatic Zip-Aligning with embedded `zipalign` *(New)* +- Single-click App Install to device - Frameworks Support ### Download @@ -16,14 +18,18 @@ ### Requirements - **JDK >= 8** - **Apktool** - [Download](https://bitbucket.org/iBotPeaches/apktool/downloads/) latest `apktool.jar` and put it in the following path: + Download latest [`apktool.jar`](https://bitbucket.org/iBotPeaches/apktool/downloads/) and put it in the following path: - Linux: `/home/{your-user-name}/.apkstudio/vendor/apktool.jar` - Windows: `C:\Users\{your-user-name}\.apkstudio\vendor\apktool.jar` If you've `apktool.jar` already installed in the specified path, replace it with the new one and execute the following command in same path: `java -jar apktool.jar empty-framework-dir --force` -- **adb** driver and **zipalign** utility +- **uber-apk-signer** + Download latest [`uber-apk-signer.jar`](https://github.com/patrickfav/uber-apk-signer/releases) and put it in the following path: + - Linux: `/home/{your-user-name}/.apkstudio/vendor/uber-apk-signer.jar` + - Windows: `C:\Users\{your-user-name}\.apkstudio\vendor\uber-apk-signer.jar` +- **adb** (optional) and **zipalign** (linux_x86 only) - Ubuntu: `sudo apt-get install android-tools-adb zipalign` - - Windows: [adb driver](https://lifehacker.com/the-easiest-way-to-install-androids-adb-and-fastboot-to-1586992378) [zipalign](https://stackoverflow.com/questions/36916462/how-to-zipalign-the-apk-file-in-windows) + - Windows: [adb driver](https://lifehacker.com/the-easiest-way-to-install-androids-adb-and-fastboot-to-1586992378) ### Links - [Building (Command line)](https://github.com/Surendrajat/ApkStudio/wiki/Building#building-linux) @@ -33,14 +39,13 @@ - [Special Thanks](https://github.com/Surendrajat/ApkStudio/wiki/SpecialThanks) ### TODO -- Replacing `jarsigner` with [`uber-apk-signer`](https://github.com/patrickfav/uber-apk-signer) ([#issue96](https://github.com/vaibhavpandeyvpz/apkstudio/issues/96)) -- [Framework installation](https://ibotpeaches.github.io/Apktool/documentation/#frameworks) within IDE -- Code Completion +- [Framework management](https://ibotpeaches.github.io/Apktool/documentation/#frameworks) within IDE +- Opcode Hex Viewer ### Screenshots ->![Screenshot #1](https://raw.githubusercontent.com/surendrajat/apkstudio/master/external/screenshots/apkstudio000.png "Screenshot #1") +>![Screenshot #1](https://raw.githubusercontent.com/surendrajat/apkstudio/master/external/screenshots/apkstudio881.png "Screenshot #1") ->![Screenshot #2](https://raw.githubusercontent.com/surendrajat/apkstudio/master/external/screenshots/apkstudio002.png "Screenshot #2") +>![Screenshot #2](https://raw.githubusercontent.com/surendrajat/apkstudio/master/external/screenshots/apkstudio882.png "Screenshot #2") ### Disclaimer >Same as [Apktool](http://ibotpeaches.github.io/Apktool/), **APK Studio** is neither intended for piracy nor other non-legal uses. It could be used for localizing, adding some features or support for custom platforms, analyzing applications & much more. \ No newline at end of file diff --git a/apkstudio.pro b/apkstudio.pro index b518061..d0c807a 100644 --- a/apkstudio.pro +++ b/apkstudio.pro @@ -22,7 +22,6 @@ HEADERS += \ include/flickcharm.h \ include/ide.h \ include/installrunnable.h \ - include/jarsigner.h \ include/java.h \ include/macros.h \ include/menubar.h \ @@ -43,7 +42,7 @@ HEADERS += \ include/toolbar.h \ include/viewer.h \ include/widgetbar.h \ - include/zipalign.h + include/uberapksigner.h OTHER_FILES += \ .gitignore \ @@ -88,7 +87,6 @@ SOURCES += \ src/flickcharm.cpp \ src/ide.cpp \ src/installrunnable.cpp \ - src/jarsigner.cpp \ src/java.cpp \ src/main.cpp \ src/menubar.cpp \ @@ -109,7 +107,7 @@ SOURCES += \ src/toolbar.cpp \ src/viewer.cpp \ src/widgetbar.cpp \ - src/zipalign.cpp + src/uberapksigner.cpp TARGET = apkstudio diff --git a/external/screenshots/apkstudio000.png b/external/screenshots/apkstudio000.png deleted file mode 100644 index 66ad766..0000000 Binary files a/external/screenshots/apkstudio000.png and /dev/null differ diff --git a/external/screenshots/apkstudio002.png b/external/screenshots/apkstudio002.png deleted file mode 100644 index 77aa6d5..0000000 Binary files a/external/screenshots/apkstudio002.png and /dev/null differ diff --git a/external/screenshots/apkstudio881.png b/external/screenshots/apkstudio881.png new file mode 100644 index 0000000..c9c4213 Binary files /dev/null and b/external/screenshots/apkstudio881.png differ diff --git a/external/screenshots/apkstudio882.png b/external/screenshots/apkstudio882.png new file mode 100644 index 0000000..ebf388b Binary files /dev/null and b/external/screenshots/apkstudio882.png differ diff --git a/include/constants.h b/include/constants.h index 534b53e..6e0eebf 100644 --- a/include/constants.h +++ b/include/constants.h @@ -22,7 +22,7 @@ #define HIGHLIGHTER_THEME "default.theme" #define HIGHLIGHTER_STYLE_WHITESPACES "whitespaces" -#define PREF_DEFAULT_JAVA_HEAP 128 +#define PREF_DEFAULT_JAVA_HEAP 256 #define PREF_DEFAULT_TAB_STOP_WIDTH 4 #define PREF_DEFAULT_TEXT_ENCODING "UTF-8" @@ -54,6 +54,7 @@ #define REGEX_LF "[\\r\\n]" #define REGEX_ADB_VERSION "^.*(\\d+)\\.(\\d+)\\.(\\d+)$" #define REGEX_APKTOOL_VERSION "^(\\d+)\\.(\\d+)\\.(\\d+)$" +#define REGEX_UBERAPKTOOL_VERSION "^.*(\\d+)\\.(\\d+)\\.(\\d+)$" #define REGEX_JAVA_VERSION "^.*\"(\\d+)\\.(\\d+)\\.(\\d+)_(\\d+)\"$" #define REGEX_THEME_STYLE "\\b([a-z]+)\\:\\s*([0-9a-z#]+)\\b" #define REGEX_WHITESPACE "\\s+" diff --git a/include/ide.h b/include/ide.h index 132e8d0..4970a1d 100644 --- a/include/ide.h +++ b/include/ide.h @@ -71,6 +71,7 @@ public Q_SLOTS: void onMenuBarProjectInstall(); void onMenuBarProjectReload(); void onMenuBarProjectSignExport(); + void onToolBarProjectSign(); void onOpenApk(const QString &apk); void onOpenDir(const QString &project); void onRunnableStarted(); diff --git a/include/jarsigner.h b/include/jarsigner.h deleted file mode 100644 index 5eb0b09..0000000 --- a/include/jarsigner.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef JARSIGNER_H -#define JARSIGNER_H - -#include "macros.h" -#include "process.h" - -APP_NAMESPACE_START - -class JarSigner : public Process -{ -private: - static JarSigner *_self; -private: - explicit JarSigner(QObject *parent = 0); -public: - static JarSigner *get(); - Result sign(const QString &src, const QString &dest, const QString &keystore, const QString &keystorePass, const QString &key, const QString &keyPass = QString()); -}; - -APP_NAMESPACE_END - -#endif // JARSIGNER_H diff --git a/include/uberapksigner.h b/include/uberapksigner.h new file mode 100644 index 0000000..529b58f --- /dev/null +++ b/include/uberapksigner.h @@ -0,0 +1,31 @@ +#ifndef UBERAPKSIGNER_H +#define UBERAPKSIGNER_H + +#include +#include "macros.h" +#include "java.h" + +APP_NAMESPACE_START + +class UberApkSigner : public Java +{ +private: + QString _jar; + static UberApkSigner *_self; +private: + explicit UberApkSigner(QObject *parent = 0); +public: + // Result build(const QString &project, const QString &apk); + // Result decode(const QString &apk, const QString &project, const QString &framework, const bool sources, const bool resources); + Result sign(const QString &src, const QString &keystore, const QString &keystorePass, const QString &key, const QString &keyPass = QString()); + Result signDebug(const QString &src); + inline Result exec(const QString &arg) { return exec(QStringList(arg)); } + Result exec(const QStringList &args = QStringList()); + static UberApkSigner *get(); + QString getVersion(); +}; + +APP_NAMESPACE_END + +#endif // UBERAPKSIGNER_H + diff --git a/include/zipalign.h b/include/zipalign.h deleted file mode 100644 index b107c19..0000000 --- a/include/zipalign.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef ZIPALIGN_H -#define ZIPALIGN_H - -#include "macros.h" -#include "process.h" - -APP_NAMESPACE_START - -class ZipAlign : public Process -{ -private: - static ZipAlign *_self; -private: - explicit ZipAlign(QObject *parent = 0); -public: - Result align(const QString &src, const QString &dest); - static ZipAlign *get(); -}; - -APP_NAMESPACE_END - -#endif // ZIPALIGN_H diff --git a/res/all.qrc b/res/all.qrc index 8fd0b5f..35d5923 100644 --- a/res/all.qrc +++ b/res/all.qrc @@ -5,6 +5,7 @@ highlight/default.theme highlight/numbers.def highlight/smali.def + highlight/java.def highlight/strings.def highlight/xml.def highlight/yml.def diff --git a/res/highlight/java.def b/res/highlight/java.def new file mode 100644 index 0000000..af5c0bb --- /dev/null +++ b/res/highlight/java.def @@ -0,0 +1,8 @@ +namespace ^(import|package)\s[a-zA-Z0-9\.]+\b +keywords \b(abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|extends|false|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|native|new|null|package|private|protected|public|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|transient|true|try|void|volatile|while)\b +constants \b[A-Z0-9_]+\b +annotations @[a-zA-Z]+\b +@include numbers.def +@include strings.def +comments //[^\n]* +comments#multiline /\*|\*/ diff --git a/res/lang/en.ts b/res/lang/en.ts index 5088827..2435acb 100644 --- a/res/lang/en.ts +++ b/res/lang/en.ts @@ -300,7 +300,7 @@ sign_export - Sign/Export + Sign/Import install @@ -380,6 +380,10 @@ version_apktool APK Tool: %1 + + version_uberapksigner + APK Signer: %1 + version_java Java: %1 @@ -461,7 +465,7 @@ sign_export - Sign/Export APK + Import KeyStore(Optional) and Sign APK quit diff --git a/src/consoledock.cpp b/src/consoledock.cpp index 9f93959..8435947 100644 --- a/src/consoledock.cpp +++ b/src/consoledock.cpp @@ -3,9 +3,8 @@ #include "include/apktool.h" #include "include/consoledock.h" #include "include/constants.h" -#include "include/jarsigner.h" +#include "include/uberapksigner.h" #include "include/qrc.h" -#include "include/zipalign.h" APP_NAMESPACE_START @@ -35,12 +34,11 @@ ConsoleDock::ConsoleDock(QWidget *parent) _connections << connect(Adb::get(), &Process::executing, this, &ConsoleDock::onExecuting); _connections << connect(ApkTool::get(), &Process::executed, this, &ConsoleDock::onExecuted); _connections << connect(ApkTool::get(), &Process::executing, this, &ConsoleDock::onExecuting); - _connections << connect(JarSigner::get(), &Process::executed, this, &ConsoleDock::onExecuted); - _connections << connect(JarSigner::get(), &Process::executing, this, &ConsoleDock::onExecuting); + _connections << connect(UberApkSigner::get(), &Process::executed, this, &ConsoleDock::onExecuted); + _connections << connect(UberApkSigner::get(), &Process::executing, this, &ConsoleDock::onExecuting); _connections << connect(Java::get(), &Process::executed, this, &ConsoleDock::onExecuted); _connections << connect(Java::get(), &Process::executing, this, &ConsoleDock::onExecuting); - _connections << connect(ZipAlign::get(), &Process::executed, this, &ConsoleDock::onExecuted); - _connections << connect(ZipAlign::get(), &Process::executing, this, &ConsoleDock::onExecuting); + } void ConsoleDock::onExecuted(const Process::Result &r) diff --git a/src/ide.cpp b/src/ide.cpp index 1006cec..33d60d1 100644 --- a/src/ide.cpp +++ b/src/ide.cpp @@ -360,6 +360,20 @@ void Ide::onMenuBarProjectSignExport() } } +void Ide::onToolBarProjectSign() +{ + + if (_apk.isNull() || _apk.isEmpty()) + { + QMessageBox::warning(this, __("no_apk", "titles"), __("no_apk", "messages"), QMessageBox::Close); + } + else + { + Preferences *p = Preferences::get(); + Runner::get()->add(new SignRunnable(_apk, p->signKeystore(), p->signKeystorePass(), p->signKey(), p->signKeyPass(), this)); + } +} + void Ide::onOpenApk(const QString &p) { (new PreOpenApk(p, this))->exec(); diff --git a/src/jarsigner.cpp b/src/jarsigner.cpp deleted file mode 100644 index d636101..0000000 --- a/src/jarsigner.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "include/constants.h" -#include "include/jarsigner.h" - -APP_NAMESPACE_START - -JarSigner* JarSigner::_self = NULL; - -JarSigner::JarSigner(QObject *parent) -#ifdef Q_OS_WIN - : Process("jarsigner.exe", parent) -#else - : Process("jarsigner", parent) -#endif -{ -} - -Process::Result JarSigner::sign(const QString &s, const QString &d, const QString &ks, const QString &ksp, const QString &k, const QString &kp) -{ - QStringList args("-verbose"); - args << "-sigalg" << "SHA1withRSA" << "-digestalg" << "SHA1"; - args << "-keystore" << ks << "-storepass" << ksp; - if (!kp.isEmpty()) - { - args << "-keypass" << kp; - } - return exec(args << "-signedjar" << d << s << k); -} - -JarSigner *JarSigner::get() -{ - if (!_self) - { - _self = new JarSigner(); - } - return _self; -} - -APP_NAMESPACE_END diff --git a/src/signrunnable.cpp b/src/signrunnable.cpp index f055e76..c526f68 100644 --- a/src/signrunnable.cpp +++ b/src/signrunnable.cpp @@ -1,8 +1,7 @@ #include -#include "include/jarsigner.h" +#include "include/uberapksigner.h" #include "include/pathutils.h" #include "include/signrunnable.h" -#include "include/zipalign.h" APP_NAMESPACE_START @@ -16,20 +15,14 @@ SignRunnable::SignRunnable(const QString &s, const QString &ks, const QString &k void SignRunnable::run() { emit runnableStarted(); - QString tmp = PathUtils::temp("signed.apk"); - Process::Result r = JarSigner::get()->sign(_src, tmp, _keystore, _keystorePass, _key, _keyPass); + Process::Result r; + if(_keystore.isEmpty()) + r = UberApkSigner::get()->signDebug(_src); + else + r = UberApkSigner::get()->sign(_src, _keystore, _keystorePass, _key, _keyPass); if (r.code == 0) { - r = ZipAlign::get()->align(tmp, _src); - if (r.code == 0) - { - emit signSuccess(_src); - } - else - { - emit signFailure(_src); - } - QFile::remove(tmp); + emit signSuccess(_src); } else { diff --git a/src/statusbar.cpp b/src/statusbar.cpp index 57a6d53..86913d2 100644 --- a/src/statusbar.cpp +++ b/src/statusbar.cpp @@ -1,5 +1,6 @@ #include "include/adb.h" #include "include/apktool.h" +#include "include/uberapksigner.h" #include "include/java.h" #include "include/statusbar.h" #include "include/qrc.h" @@ -11,12 +12,15 @@ StatusBar::StatusBar(QWidget *parent) { QLabel *adb; QLabel *apkTool; + QLabel *uberApkSigner; QLabel *java; addPermanentWidget(java = new QLabel(this)); addPermanentWidget(new StatusBarSeparator(this)); addPermanentWidget(adb = new QLabel(this)); addPermanentWidget(new StatusBarSeparator(this)); addPermanentWidget(apkTool = new QLabel(this)); + addPermanentWidget(new StatusBarSeparator(this)); + addPermanentWidget(uberApkSigner = new QLabel(this)); addPermanentWidget(new QWidget(this), 1); addPermanentWidget(_message = new QLabel(this)); setContentsMargins(4, 0, 4, 0); @@ -38,6 +42,14 @@ StatusBar::StatusBar(QWidget *parent) { apkTool->setText(__("version_apktool", "statusbar", "n/a")); } + if ((v = UberApkSigner::get()->getVersion()).isEmpty() == false) + { + uberApkSigner->setText(__("version_uberapksigner", "statusbar", v)); + } + else + { + uberApkSigner->setText(__("version_uberapksigner", "statusbar", "n/a")); + } if ((v = Java::get()->getVersion()).isEmpty() == false) { java->setText(__("version_java", "statusbar", v)); diff --git a/src/toolbar.cpp b/src/toolbar.cpp index e166aa2..bfd33ae 100644 --- a/src/toolbar.cpp +++ b/src/toolbar.cpp @@ -13,7 +13,7 @@ ToolBar::ToolBar(QWidget *p) addAction(Qrc::icon("toolbar_save"), __("save", "toolbar"), p, SLOT(onMenuBarFileSave())); addSeparator(); addAction(Qrc::icon("toolbar_build"), __("build", "toolbar"), p, SLOT(onMenuBarProjectBuild())); - addAction(Qrc::icon("toolbar_sign"), __("sign", "toolbar"), p, SLOT(onMenuBarProjectSignExport())); + addAction(Qrc::icon("toolbar_sign"), __("sign", "toolbar"), p, SLOT(onToolBarProjectSign())); addSeparator(); addAction(Qrc::icon("toolbar_terminal"), __("terminal", "toolbar"), p, SLOT(onMenuBarFileTerminal())); addAction(Qrc::icon("toolbar_settings"), __("settings", "toolbar"), p, SLOT(onMenuBarEditSettings())); diff --git a/src/uberapksigner.cpp b/src/uberapksigner.cpp new file mode 100644 index 0000000..9971a47 --- /dev/null +++ b/src/uberapksigner.cpp @@ -0,0 +1,67 @@ +#include "include/uberapksigner.h" +#include "include/constants.h" +#include "include/pathutils.h" +#include "include/textutils.h" + +APP_NAMESPACE_START + +UberApkSigner* UberApkSigner::_self = NULL; + +UberApkSigner::UberApkSigner(QObject *parent) + : Java(parent) +{ + _jar = PathUtils::find("uber-apk-signer.jar"); +} + +Process::Result UberApkSigner::sign(const QString &s, const QString &ks, const QString &ksp, const QString &k, const QString &kp) +{ + QStringList args("--debug"); + args << "-ks" << ks << "--ksPass" << ksp; + if (!kp.isEmpty()) + { + args << "--ksKeyPass" << kp; + } + return exec(args << "-a" << s << "--ksAlias" << k << "--overwrite" << "--allowResign"); +} + +Process::Result UberApkSigner::signDebug(const QString &s) +{ + QStringList args("--debug"); + return exec(args << "-a" << s << "--overwrite" << "--allowResign"); +} + +UberApkSigner *UberApkSigner::get() +{ + if (!_self) + { + _self = new UberApkSigner(); + } + return _self; +} + +Process::Result UberApkSigner::exec(const QStringList &a) +{ + return Java::exec(QStringList("-jar") << _jar << a); +} + + +QString UberApkSigner::getVersion() +{ + Process::Result r = exec("-version"); + QRegularExpression rgx(REGEX_UBERAPKTOOL_VERSION); + foreach (const QString &l, r.output) + { + QRegularExpressionMatch m = rgx.match(l); + if (m.hasMatch()) + { + QString v("%1.%2.%3"); + for (int i = 1; i <= 3; i++) + { v = v.arg(m.captured(i)); } + return v; + } + } + return QString(); +} + +APP_NAMESPACE_END + diff --git a/src/zipalign.cpp b/src/zipalign.cpp deleted file mode 100644 index b7e08eb..0000000 --- a/src/zipalign.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "include/zipalign.h" -#include "include/constants.h" - -APP_NAMESPACE_START - -ZipAlign* ZipAlign::_self = NULL; - -ZipAlign::ZipAlign(QObject *parent) -#ifdef Q_OS_WIN - : Process("zipalign.exe", parent) -#else - : Process("zipalign", parent) -#endif -{ -} - -Process::Result ZipAlign::align(const QString &src, const QString &dest) -{ - return exec(QStringList("-f") << "-v" << "4" << src << dest); -} - -ZipAlign *ZipAlign::get() -{ - if (!_self) - { - _self = new ZipAlign(); - } - return _self; -} - -APP_NAMESPACE_END