Android逆向之实战逆向APP

您所在的位置:网站首页 逆向APP后加入自己的登录模块 Android逆向之实战逆向APP

Android逆向之实战逆向APP

2024-04-20 20:50| 来源: 网络整理| 查看: 265

前言

Android逆向之指令集和CPU架构

Android逆向之必备前置知识

经过前2篇逆向前置知识的铺垫之后,我们终于要开始逆向实操了。以下操作主要是体现一下逆向的流程以及实操,为了安全考虑,并不会直接给出APP名称。从目标出发,一步步去完成我们的目标,回头再看,大家就会发现,不过如此罢了。

逆向目标

获取APP中的某个功能,例如点击加载模型,模型会自带一些事件触发逻辑。博主的目的是拿到这部分事件逻辑的代码,参考一下。例如下图的加载模型。

image.png

环境准备 安装有linux系统的电脑,此处以manjaro linux 23.1.0-1为例。 安装有某APP的手机,此处以iPhone 13为例。 linux系统上安装以下程序。 大家可以看到,安装的程序都是我们前置知识里面提过的,是必不可少的工具。 sudo pacman -Sy binutils python jadx radare2 sudo pip install mitmproxy 确保电脑和手机处于同一个网络,方便抓包操作。

开始逆向之旅

抓包 💻 在终端执行mitmweb命令, 此时会在默认浏览器中打开一个新的标签页,如下所示:

image.png

📱 在设置->无线局域网,点击已经连接的Wifi名称右边的编辑按钮,如下所示:

image.png

📱 在进入Wifi编译页之后,滚动到页面最下方,点击配置代理,选择手动,之后在出现的服务器输入框中输入电脑的IP地址,** 端口输入框中填写8080**,点击存储,如下所示:

image.png

📱 在默认浏览器中,此处为Safari, 打开 mitm.it, 滚动页面到对应平台处,此处为iOS, 按照页面提示的步骤,安装mitmproxy需要的CA证书,如下所示:

image.png

📱 打开 某 App,点击一个没有下载过的模型,触发下载,如下所示:

image.png

💻 在mitmweb打开的浏览器标签页,分析抓到的包列表,注意到有一个下载zip的请求,猜测是该模型对应的资源文件,如下所示:

image.png

💻 在终端执行以下命令,下载zip文件: wget http://cdn3.cooolar.com/model/b/e/5/5c6a65be57f782510b26bdb4/ios_192/5c6a65be57f782510b26bdb4.zip 💻 在终端执行以下命令,解压zip文件: unzip 5c6a65be57f782510b26bdb4.zip

提示需要输入解压密码,如下所示,这似乎进一步证实该zip包就是我们需要的模型资源文件。

image.png 接下来我们需要找到该zip包解压需要的密码。

寻找解压密码 💻 下载网址可以自己在网上找,当星网-绿色软件_最新绿色下载软件_免费软件下载网站 - 当星网这个网址可以下载到部分的APK文件。下载某APP的APK, 保存为 magicxx.apk。 💻** **终端执行以下命令,解压出APK相应的资源文件和Java源码: 还记得吗,就是我们前置知识中说的jadx反编译工具 jadx -d magicxx magicxx.apk cd magicxx 💻 终端执行以下命令,查找zip相关的Java文件: find sources -name "*zip*.java"

得到以下结果:

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函数:

image.png

💻 终端执行以下命令,搜索Java源码中使用setPassword函数的源码: grep -r setPassword sources

得到以下结果:

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函数起始地址处, 如下所示:!

image.png

💻 在radare2程序中,执行pdf指令,查看Java_com_xx_magicalar_common_Constant_getZipSecret函数的实现, 如下所示: 没错,开始需要汇编知识了

image.png

💻 分析函数代码,看到在libnative-lib.so文件的0xf8d5处有一串字符串d(EcaK#9cBQL,就是需要的secret:

image.png

解压密码生成函数

💻 根据得到的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