Android逆向之实战逆向APP |
您所在的位置:网站首页 › 逆向APP后加入自己的登录模块 › Android逆向之实战逆向APP |
前言
Android逆向之指令集和CPU架构 Android逆向之必备前置知识 经过前2篇逆向前置知识的铺垫之后,我们终于要开始逆向实操了。以下操作主要是体现一下逆向的流程以及实操,为了安全考虑,并不会直接给出APP名称。从目标出发,一步步去完成我们的目标,回头再看,大家就会发现,不过如此罢了。 逆向目标获取APP中的某个功能,例如点击加载模型,模型会自带一些事件触发逻辑。博主的目的是拿到这部分事件逻辑的代码,参考一下。例如下图的加载模型。
提示需要输入解压密码,如下所示,这似乎进一步证实该zip包就是我们需要的模型资源文件。
得到以下结果: sources/net/lingala/zip4j/unzip/Unzip.java sources/net/lingala/zip4j/unzip/UnzipEngine.java sources/net/lingala/zip4j/unzip/UnzipUtil.java sources/net/lingala/zip4j/model/UnzipParameters.java sources/net/lingala/zip4j/model/UnzipEngineParameters.java sources/com/xx/magicalar/mvp/view/model/IModelUnzipView.java sources/okio/GzipSink.java sources/okio/GzipSource.java应该是使用了net.lingala.zip4j库进行unzip操作。 💻 查看zip4j的API文档,发现有一个setPassword函数:得到以下结果: sources/net/lingala/zip4j/core/HeaderReader.java: localFileHeader.setPassword(fileHeader.getPassword()); sources/net/lingala/zip4j/core/ZipFile.java: public void setPassword(String str) throws ZipException { sources/net/lingala/zip4j/core/ZipFile.java: setPassword(str.toCharArray()); sources/net/lingala/zip4j/core/ZipFile.java: public void setPassword(char[] cArr) throws ZipException { sources/net/lingala/zip4j/core/ZipFile.java: ((FileHeader) this.zipModel.getCentralDirectory().getFileHeaders().get(i)).setPassword(cArr); sources/net/lingala/zip4j/model/ZipParameters.java: public void setPassword(String str) { sources/net/lingala/zip4j/model/ZipParameters.java: setPassword(str.toCharArray()); sources/net/lingala/zip4j/model/ZipParameters.java: public void setPassword(char[] cArr) { sources/net/lingala/zip4j/model/FileHeader.java: public void setPassword(char[] cArr) { sources/net/lingala/zip4j/model/LocalFileHeader.java: public void setPassword(char[] cArr) { sources/com/xx/magicalar/retrofitUtils/downmodel/DownLoadUtil.java: zipFile.setPassword(MD5_1.substring(MD5_1.length() - 10).toCharArray()); sources/com/xx/magicalar/retrofitUtils/scandown/ScanDownUtils.java: zipFile.setPassword(MD5_1.substring(MD5_1.length() - 10).toCharArray()); sources/android/support/design/widget/TextInputLayout.java: public void setPasswordVisibilityToggleDrawable(int i) { sources/android/support/design/widget/TextInputLayout.java: setPasswordVisibilityToggleDrawable(i != 0 ? AppCompatResources.getDrawable(getContext(), i) : null); sources/android/support/design/widget/TextInputLayout.java: public void setPasswordVisibilityToggleDrawable(Drawable drawable) { sources/android/support/design/widget/TextInputLayout.java: public void setPasswordVisibilityToggleContentDescription(int i) { sources/android/support/design/widget/TextInputLayout.java: setPasswordVisibilityToggleContentDescription(i != 0 ? getResources().getText(i) : null); sources/android/support/design/widget/TextInputLayout.java: public void setPasswordVisibilityToggleContentDescription(CharSequence charSequence) { sources/android/support/design/widget/TextInputLayout.java: public void setPasswordVisibilityToggleEnabled(boolean z) { sources/android/support/design/widget/TextInputLayout.java: public void setPasswordVisibilityToggleTintList(ColorStateList colorStateList) { sources/android/support/design/widget/TextInputLayout.java: public void setPasswordVisibilityToggleTintMode(PorterDuff.Mode mode) { sources/android/support/v4/widget/ExploreByTouchHelper.java: obtain.setPassword(obtainAccessibilityNodeInfo.isPassword()); sources/android/support/v4/view/accessibility/AccessibilityRecordCompat.java: public void setPassword(boolean z) { sources/android/support/v4/view/accessibility/AccessibilityRecordCompat.java: this.mRecord.setPassword(z); sources/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java: public void setPassword(boolean z) { sources/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java: this.mInfo.setPassword(z);其中sources/com/xx/magicalar/retrofitUtils/downmodel/DownLoadUtil.java中的setPassword调用应该是和模型下载之后解压相关的。 💻 打开sources/com/xx/magicalar/retrofitUtils/downmodel/DownLoadUtil.java,找到setPassword调用的地方,只有436行一处调用,如下所示: if (zipFile.isEncrypted()) { String substring = str.substring(0, str.indexOf(".zip")); String MD5_1 = Signature.MD5_1(new Object[]{substring.substring(substring.length() - 10), Constant.getZipSecret()}); zipFile.setPassword(MD5_1.substring(MD5_1.length() - 10).toCharArray()); } zipFile.extractAll(str2);看到密码是通过zip文件名以及Constant.getZipSecret()函数的返回值计算MD5得到的。其中zip文件名是5c6a65be57f782510b26bdb4.zip,还需要得到**Constant.getZipSecret()**返回值。 💻 终端执行以下命令,搜索Java源码中getZipSecret函数的定义: 这里涉及到了Android的native层,前置知识中提到过 grep -r getZipSecret sources得到以下结果: sources/com/xx/magicalar/retrofitUtils/downmodel/DownLoadUtil.java: String MD5_1 = Signature.MD5_1(new Object[]{substring.substring(substring.length() - 10), Constant.getZipSecret()}); sources/com/xx/magicalar/retrofitUtils/scandown/ScanDownUtils.java: String MD5_1 = Signature.MD5_1(new Object[]{substring.substring(substring.length() - 10), Constant.getZipSecret()}); sources/com/xx/magicalar/common/Constant.java: public static native String getZipSecret();看到该函数是以native方式定义在Constant.java中的。需要去APK中的so库中寻找getZipSecret()函数的实现。 so库逆向 💻 在终端执行以下命令,搜索包含getZipSecret相关信息的so库: ls resources/lib/armeabi-v7a/*.so | xargs nm -A 2>&1 | grep -v "no symbols" | c++filt | grep getZipSecret得到以下结果: resources/lib/armeabi-v7a/libnative-lib.so:000116c9 T Java_com_qjtc_magicalar_common_Constant_getZipSecret在libnative-lib.so中有Java_com_xx_magicalar_common_Constant_getZipSecret函数的定义。 💻 在终端执行以下命令,使用radare2分析libnative-lib.so: 重头戏来了,二进制文件分析 r2 resources/lib/armeabi-v7a/libnative-lib.so 💻 在radare2程序中,依次执行以下命令 aaaa:分析这个二进制文件 s sym.Java_com_qjtc_magicalar_common_Constant_getZipSecret指令 :跳转到指定函数地址然后我们跳转到Java_com_qjtc_magicalar_common_Constant_getZipSecret函数起始地址处, 如下所示:!
💻 根据得到的secret数据d(EcaK#9cBQL, 以及之前的相关的Java源码: if (zipFile.isEncrypted()) { String substring = str.substring(0, str.indexOf(".zip")); String MD5_1 = Signature.MD5_1(new Object[]{substring.substring(substring.length() - 10), Constant.getZipSecret()}); zipFile.setPassword(MD5_1.substring(MD5_1.length() - 10).toCharArray()); } zipFile.extractAll(str2);重新使用Python实现如下代码,保存成unzip.py: import hashlib import sys ZipSecret = 'd(EcaK#9cBQL' def genPassword(zipFile: str) -> str: substring = zipFile[0:zipFile.index('.zip')] md5 = hashlib.md5((substring[-10:] + ZipSecret).encode()).hexdigest() return md5[-10:] if __name__ == '__main__': print(genPassword(sys.argv[1]))在终端执行以下代码, python unzip.py 5c6a65be57f782510b26bdb4.zip得到解压密码为 1cfa866766, 执行 unzip 5c6a65be57f782510b26bdb4.zip, 输入加压密码,正常解压。最终得到如下内容: 5c6a65be57f782510b26bdb4 ├── 5c6a65be57f782510b26bdb4.bytes ├── 5c6a65be57f782510b26bdb4.png └── LuaProject └── main.lua查看5c6a65be57f782510b26bdb4.png文件,的确是我们下载的模型的缩略图。而这个main.lua就是我们想要参考的事件逻辑代码了。 总结是的,整体流程也许没有大家想象中的那么神秘,但我们确实是实现了最初的逆向目标。在整个流程中也用到了前面提到的必备前置知识。大家可以举一反三,比如逆向目标变成下载所有的模型?比如逆向目标变成更改源代码重新打包?比如逆向目标变成检测自己公司内APP的安全性? 等等。 逆向不是银弹,有逆向自然也有防御。逆向的目标并不是只有破坏,很多逆向操作反而是为了完善自己的安全策略。所以呢,技术没有好坏,有好坏的是使用技术的人。 再会了各位! end |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |