请选择 进入手机版 | 继续访问电脑版
查看: 14904|回复: 0

Android逆向-脱壳APP

[复制链接]
匿名
匿名  发表于 2023-3-14 21:03:19 |阅读模式
1.需要环境

kali环境 / 其他Linux / windows / macOSpython3.8.0frida12.8.0     / Android 8以下用这个版本 高版本直接最新frida server 12.8.0frida-tools 5.3.0objection 1.8.4Redmi5A ROMixelExperience / 其他root的均可x音漫客 app 最新版

2.kali frida 环境搭建

  • 首先安装python (python3.8.0)

  • 安装frida 12.8.0

    • pip install frida==12.8.0

  • frida-tools 5.3.0

    • pip install frida-tools==5.3.0

  • objection 1.8.4

    • pip install objection==1.8.4
3.手机安装kali frida server 12.8.0

打开 frida12.8.0

qw1.jpg


这里我们要下载Android版本的,当前下载为Android-arm64版本的根据自己的手机去选择下载好之后使用adb推送到手机上

qw2.jpg


我们使用adb进入手机运行frida server

qw3.jpg


解压之后改个名字然后需要赋予执行权限 chmod 777 fr12.8.0

qw4.jpg


./fr12.8.0 -D 后台运行frida server

qw5.jpg


继续安装x音漫客官网自行下载 adb install *.apk
4.猜测逻辑
qw6.jpg


可以看到有的是带这种有锁的,也就是开VIP后可以看的,上面的那些则可以免费观看,可以猜测一下这里有个分支判断是否是vip然后走向不同的地方,那么我们hook所有的onClick函数看看是什么逻辑
5.验证逻辑
[AppleScript] 纯文本查看 复制代码
var jclazz = null;
var jobj = null;

function getObjClassName(obj) {
    if (!jclazz) {
        var jclazz = Java.use("java.lang.Class");
    }
    if (!jobj) {
        var jobj = Java.use("java.lang.Object");
    }
    return jclazz.getName.call(jobj.getClass.call(obj));
}

function watch(obj, mtdName) {
    var listener_name = getObjClassName(obj);
    var target = Java.use(listener_name);
    if (!target || !mtdName in target) {
        return;
    }
    // send("[WatchEvent] hooking " + mtdName + ": " + listener_name);
    target[mtdName].overloads.forEach(function (overload) {
        overload.implementation = function () {
            //send("[WatchEvent] " + mtdName + ": " + getObjClassName(this));
            console.log("[WatchEvent] " + mtdName + ": " + getObjClassName(this))
            return this[mtdName].apply(this, arguments);
        };
    })
}

function OnClickListener() {
    Java.perform(function () {

        //以spawn启动进程的模式来attach的话
        Java.use("android.view.View").setOnClickListener.implementation = function (listener) {
            if (listener != null) {
                watch(listener, 'onClick');
            }
            return this.setOnClickListener(listener);
        };

        //如果frida以attach的模式进行attch的话
        Java.choose("android.view.View$ListenerInfo", {
            onMatch: function (instance) {
                instance = instance.mOnClickListener.value;
                if (instance) {
                    console.log("mOnClickListener name is :" + getObjClassName(instance));
                    watch(instance, 'onClick');
                }
            },
            onComplete: function () {
            }
        })
    })
}
setImmediate(OnClickListener);


使用frida来运行一下 如果错误请换frida版本
qw7.jpg


运行之后点击带锁的按钮会出现cn.zymk.comic.ui.adapter.DirectoryPictureAdapter.1

类名:cn.zymk.comic.ui.adapter.DirectoryPictureAdapter匿名函数1我们用jadx反编译来看下


qw8.jpg


发现并没有搜到我们想要的匿名函数或者这个类

qw9.jpg


这里可以看出来是百度加固(碰到的多了而已),那么我们先去脱下壳了
6.脱壳

这里对于壳的概念就不多做解释了,我们直接上frida-dexdump即可,frida-dexdump链接(https://github.com/hluwa/frida-dexdump)

pip install frida-dexdumpfrida-dexdump -FU 使用方式跟frida一样

qw10.jpg


这里注意一点 -FU 是前置窗口也就是手机当前打开的窗口 如果不是当前窗口请用frida-dexdump -u -f 包名

qw11.jpg


生成了很多的dex 我们过滤下带有MainActivity

qw12.jpg


可以看出来08带有MainActivity且是最大的我们直接 jadx classes08.dex 然后在在里面搜一下我们刚刚的那个类
7.继续分析原理
qw13.jpg


这里我们就可以看到了,双击过去看看他干了什么
[AppleScript] 纯文本查看 复制代码
public void onClick(final View view3) {
                Utils.noMultiClick(view3);
                if (chapterListItemBean.level > 0 && chapterListItemBean.is_release == 0) {
                    PriorityCouponUseDialog.Builder builder = new PriorityCouponUseDialog.Builder(DirectoryPictureAdapter.this.mContext, PriorityCouponUseDialog.DialogType.CLOSE);
                    int i2 = chapterListItemBean.level;
                    PriorityCouponUseDialog show = builder.setChapterData(i2, chapterListItemBean.chapter_name + " " + chapterListItemBean.chapter_title, chapterListItemBean.chapter_id, DirectoryPictureAdapter.this.comicBean.comic_id, DirectoryPictureAdapter.this.comicBean.comic_name, chapterListItemBean.create_time).setOnActionListener(new PriorityCouponUseDialog.OnActionListener() { // from class: cn.zymk.comic.ui.adapter.DirectoryPictureAdapter.1.1
                        @Override // cn.zymk.comic.view.dialog.PriorityCouponUseDialog.OnActionListener
                        public void onClickBack() {
                        }

                        @Override // cn.zymk.comic.view.dialog.PriorityCouponUseDialog.OnActionListener
                        public void onUseCouponFailed() {
                        }

                        @Override // cn.zymk.comic.view.dialog.PriorityCouponUseDialog.OnActionListener
                        public void onUseCouponSuccess() {
                            chapterListItemBean.is_release = 1;
                            DirectoryPictureAdapter.this.notifyDataSetChanged();
                        }
                    }).show();
                    if (DirectoryPictureAdapter.this.mContext instanceof BaseActivity) {
                        DirectoryPictureAdapter.this.mContext.setPriorityCouponUseDialog(show);
                        return;
                    }
                    return;
                }
                int netType = PhoneHelper.getInstance().getNetType();
                if (netType <= 1 || netType > 4) {
                    DirectoryPictureAdapter.this.jump2ReadPage(view3, chapterListItemBean);
                } else {
                    new CustomDialog.Builder(DirectoryPictureAdapter.this.mActivity).setMessage(R.string.no_wifi).setPositiveButton((CharSequence) DirectoryPictureAdapter.this.mActivity.getString(R.string.kown_no_wifi), true, new CanDialogInterface.OnClickListener() { // from class: cn.zymk.comic.ui.adapter.DirectoryPictureAdapter.1.2
                        public void onClick(CanBaseDialog canBaseDialog, int i3, CharSequence charSequence, boolean[] zArr) {
                            DirectoryPictureAdapter.this.jump2ReadPage(view3, chapterListItemBean);
                        }
                    }).setNegativeButton(R.string.cancel, true, (CanDialogInterface.OnClickListener) null).show();
                }
            }

qw14.jpg


可以看到不管什么情况都会走到这个函数我们进去看看
[AppleScript] 纯文本查看 复制代码
public void jump2ReadPage(View view, ChapterListItemBean chapterListItemBean) {
        ComicBean comicBean;
        UserBean userBean = App.getInstance().getUserBean();
        if (this.comicBean != null && userBean != null && chapterListItemBean.is_vip == 1 && !Utils.isVip(userBean.isvip)) {
            PayChapterActivity.startActivity((Activity) this.mActivity, this.comicBean.comic_name, this.comicBean.comic_id, chapterListItemBean, Constants.ACTION_BOUGHT_SUCCESS_FROM_DETAIL_2_READ);
        } else if (chapterListItemBean.isRecharge || chapterListItemBean.price <= 0 || (comicBean = this.comicBean) == null) {
            gotoReadPage(view, chapterListItemBean);
        } else if (userBean != null) {
            a.e("userBean.isvip" + userBean.isvip);
            if (Utils.isVip(userBean.isvip) && Utils.chapterChargeVip(chapterListItemBean)) {
                gotoReadPage(view, chapterListItemBean);
            } else if (SetConfigBean.getAutoBuy(this.mActivity)) {
                this.mActivity.buyThisChapter(chapterListItemBean);
            } else {
                PayChapterActivity.startActivity((Activity) this.mActivity, this.comicBean.comic_name, this.comicBean.comic_id, chapterListItemBean, Constants.ACTION_BOUGHT_SUCCESS_FROM_DETAIL_2_READ);
            }
        } else {
            PayChapterActivity.startActivity((Activity) this.mActivity, comicBean.comic_name, this.comicBean.comic_id, chapterListItemBean, Constants.ACTION_BOUGHT_SUCCESS_FROM_DETAIL_2_READ);
        }
    }


这里的代码就比较清晰了,可以看到很多的PayChapterActivity.startActivity,应该就是跳转到付款窗口那么我们就让Utils.isVip(userBean.isvip)这个函数返回true看看是否会走到gotoReadPage,这里我们需要写js的frida代码,需要安装nodenode链接
8.frida hook 关键函数
qw15.jpg

[AppleScript] 纯文本查看 复制代码
function HookVip() {
    Java.use("cn.zymk.comic.utils.Utils").isVip.implementation = function (number) {
        // 先看看他原始返回了什么
        var result = this.isVip(number);
        console.log("result = ", result);
        return result;
    }
}

function main() {
    // 主要hook和java相关的函数都需要包含在Java.perform中
    Java.perform(() => {
        HookVip();
    })
}

setImmediate(main)


frida -UF -l zymk.js 运行看看

qw16.jpg


可以看到原始返回是false 那么我们把他的返回值改了会怎么样呢
[AppleScript] 纯文本查看 复制代码
function HookVip() {
    Java.use("cn.zymk.comic.utils.Utils").isVip.implementation = function (number) {
        // 先看看他原始返回了什么
        // var result = this.isVip(number);
        // console.log("result = ", result);
        // return result;
        return true;
    }
}

function main() {
    Java.perform(() => {
        HookVip();
    })
}


setImmediate(main)
qw17.jpg

qw18.jpg


可以发现我们先跳到了内容界面然后里面,过了几秒又跳到了充值界面,那么这里试试直接把这个充值界面ret掉看看他能不能过去这个验证

qw19.jpg

qw20.jpg


可以发现已经可以正常的观看了

[AppleScript] 纯文本查看 复制代码
function HookVip() {
    Java.use("cn.zymk.comic.utils.Utils").isVip.implementation = function (number) {
        // 先看看他原始返回了什么
        // var result = this.isVip(number);
        // console.log("result = ", result);
        // return result;
        return true;
    }
}

function HookPayChapterActivity(){
    Java.use("cn.zymk.comic.ui.mine.PayChapterActivity").startActivity.overload('android.app.Activity', 'java.lang.String', 'java.lang.String', 'cn.zymk.comic.model.ChapterListItemBean', 'java.lang.String').implementation =
    function(activity, str, str2, chapterListItemBean, str3, z){
        return;
      
    }
}

function main() {
    Java.perform(() => {
        HookVip();
        HookPayChapterActivity();
    })
}

setImmediate(main)


9.总结

逆向的过程中要找到自己需要的关键点,然后跟着关键点去往下寻找找到突破口,在遇到加固或者混淆的时候尝试去dump下dex,如果遇到了函数抽取就要去主动调用下让他走一个过程再去dump,hook所有的onClick可以让我们更快的去定位到关键位置,如何去分辨dump的dex中哪个是我们想要的,可以看相关的Activity去排除




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

旗下站点

邮箱系统

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

Archiver|手机版|小黑屋| ( 苏ICP备2021031567号 )

GMT+8, 2024-12-11 18:23 , Processed in 0.021392 second(s), 13 queries , Gzip On, MemCache On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部