本文为纯技术分享,文章内容不涉黄,适合对xposed有兴趣的读者
逆向某视频app(一)
逆向某视频app(二)
目录
- 前言
- xposed破解
- 1.反编译apk
- 2.分析代码
前言
有一段时间没写博客了,6月份的时候入职了一家做逆向的公司,很长一段时间都处在高度紧张的工作状态,连着两个月经常加班到10点,虽然有点辛苦,但是学到的东西更多,现在有一段时间要闲下来,趁着这个机会把学到的东西实践一下,破解一个视频app犒劳一下广大的程序员们。这次破解我会选择两种方式:使用xposed和修改smali,使用xposed方便快捷,但是需要root手机和安装xposed框架,而修改smali比较麻烦,但是可以随意安装在任何手机上,并且不收手机环境限制。这篇文章先介绍使用xposed。
需要apk文件的可以给我留言。
xposed破解
xposed是一个hook框架,可以在不改变app的情况下加入我们自己的逻辑(在方法前和方法后加入自己的逻辑,类似于代理模式),具体使用方法我就不一一介绍了,网上一大堆,下面正式开始。
1.反编译apk
使用jadx打开apk文件,发现源码如下:
熟悉逆向的朋友们肯定猜到了,这个apk使用了360加固,所以反编译出来只有360加固的几个类,看不到目标源码。
那怎么办?
既然加固了,第一步就是要给它脱壳
关于脱壳可以参考这篇文章:Android APK脱壳–腾讯乐固、360加固一键脱壳
脱壳后拿到的dex文件如下:
总共有4个dex文件,我们可以在jadx中打开这些文件,dex会自动被转换成java代码,我们打开其中一个,如下:
可以看到代码已经混淆了,不仅加固还混淆了代码,有必要吗?
2.分析代码
想要破解一个app,首先要掌握工具的使用,但是这并不是最重要的,最重要的是要有逆向的思想,下面我们来分析如何破解这个app。
这个app在播放视频的时候会检查你是否是会员,如果不是会员在播放一分钟之后会弹出下面的提示并停止播放:
我们想要继续播放就要找到他的判断逻辑并跳过他,代码已经有了,但是我们不知道这个页面是哪个Activity,这时我们可以借助一个工具查看当前页面是哪个Activity,android-TopActivity,在手机上运行并打开这个app右上角就会显示当前页面所在的路径:
找到了位置,我们去看一下这个Activity,注意我们有5个dex文件,这个文件可能在其中的任何一个里面,耐心找一下并不难。
打开这个文件之后发现里面只有七十多行代码,都围绕着开头声明的**“qa”**对象,我们到这个对象中看一下:
刚打开这个文件就发现了两个,不知道有没有人用过GSYVideoPlayer这个播放框架,在github上star还挺高的,这里就确定这里就是播放的实现类。
简单看了一下这个类的成员变量,发现有几个有用的变量
private VideoDetail K;
private UserInfoBean L;
AdVideoPlayer Y;
VideoPlayer f566g;
从字面意思理解VideoDetail是存放视频信息的类,UserInfoBean是存放用户信息的类,AdVideoPlayer是广告播放类,VideoPlayer是真正的视频播放类,到他们各自的代码中看发现确实如猜想一样,对我们最重要的类是VideoPlayer,我们就看看哪里调用了VideoPlayer,在qa中搜索关键在**“f566g”**,
注意这里有一个log:
Log.i(“jinmu”, “ad播放完结的监听”);
广告播放完之后是不是就要开始播放视频了呢?继续看代码发现有一个setVideoVipFlagState方法值得注意,暂且放着继续往下看,第380行设置了视频的cover(封面),第390行筛选一个清晰度并设置该清晰度下的播放url,第395行设置准备完成后就开始播放,最后第401行还设置了水印。
这其中最可疑的就是setVideoVipFlagState方法,这个方法的参数是视频信息的VipFlag,他是不是标识该视频是否是vip视频(因为app里有免费视频) 我们到VideoPlayer中看一下这个方法:
public void setVideoVipFlagState(int i2) {
xb = i2;
}
这是一个set方法,这里将字段的值给了xb,我们在VideoPlayer中搜一下xb:
只有5个地方使用到了它,其中四个地方是赋值,一个地方是使用,上图510行就是使用的地方,这里如果xb等于1并且其他几个地方都为true就会去执行 getCurrentPlayer().b()这段代码,我们看一下这段代码做了什么:
public void b() {
if (this.f1172j == 1) {
this.A = true;
}
try {
if (getGSYVideoManager() != null && getGSYVideoManager().isPlaying()) {
setStateAndUi(5);
this.q = getGSYVideoManager().getCurrentPosition();
if (getGSYVideoManager() != null) {
getGSYVideoManager().pause();
}
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
这里只是暂停播放视频的操作,现在我们猜想Sa()是检查是否是会员的方法,如果不是就会暂停播放,我们来验证一下我们的猜想。既然getCurrentPlayer().b()是暂停播放的语句,那么我们不执行它不就行了,也就是不进Sa()的if语句里面,我们通过xposed hook setVideoVipFlagState方法,让xb的值始终为0,这样就永远也不会走到if语句里面:
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
String packageName = loadPackageParam.packageName;
String processName = loadPackageParam.processName;
Log.e("processName", "process:" + processName);
Log.e("packageName", "package:" + packageName);
if (packageName.equals(HUANGGUA_PACKAGE_NAME)) {
XposedHelpers.findAndHookMethod("com.one.venus.video.VideoPlayer", loadPackageParam.classLoader
, "setVideoVipFlagState",
int.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Log.e(TAG, "beforeHookedMethod:setVideoVipFlagState " + param.args[0]);
param.args[0] = 0;
}
});
}
}
然后其实会发现一个问题,class not found,找了很久,最后才反应过来,因为apk经过加固,必须要用壳的ClassLoader来加载类,因为真正的代码是360加固程序启动后它去加载真正的dex文件的。
这里我们可以hook StubApp 的 attachBaseContext 方法:
public static void hook(XC_LoadPackage.LoadPackageParam loadPackageParam) {
if (classLoader == null) {
try {
//腾讯加固,需要获取对应classloader
XposedHelpers.findAndHookMethod(XposedHelpers.findClass("com.stub.StubApp", loadPackageParam.classLoader),
"attachBaseContext", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
//获取到Context对象,通过这个对象来获取classloader
Context context = (Context) param.args[0];
//获取classloader,之后hook加固后的就使用这个classloader
classLoader = context.getClassLoader();
Log.e(TAG, "afterHookedMethod:classloader " + classLoader);
XposedHelpers.findAndHookMethod("com.one.venus.video.VideoPlayer", classLoader
, "setVideoVipFlagState",
int.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Log.e(TAG, "beforeHookedMethod:setVideoVipFlagState " + param.args[0]);
param.args[0] = 0;
}
});
}
});
} catch (Exception e) {
Log.e(TAG, "hookClassLoader: " + e.getMessage());
}
}
}
当我们再次观看视频的时候发现,卧槽可以。
观看一段时间后发现不能切换清晰度,切换时会提醒“你不是vip会员”,这怎么行,我都是要看超清的,最后找到在UserInfoBean中有一个isExpired参数,简单来说这个参数决定了你是不是会员,在播放视频和切换清晰度的时候都能生效,改这一个就行了。
XposedHelpers.findAndHookMethod("com.one.venus.entity.UserInfoBean", classLoader
, "getIsExpired",
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return 1;
}
});
我们hook UserInfoBean中的getIsExpired方法,让他始终返回1,试了一下,卧槽可以,现在可以看1080p了。
至于怎么找到isExpired这个参数的有兴趣的可以自己找找。
到这里就已经完成使用xposed破解这个app了。
有兴趣的可以看我的下一篇文章:逆向某视频app(二)
更多推荐
逆向某视频app(一)
发布评论