破解某交(y)友(p)app的VIP&&半自动im机器人
案例
就不放了,某交(y)友(p)app
致谢
Youpk[1]环信IM文档[2]hanbing&&r0ysue
加固简单分析
拿到app就先拿到jadx中分析,发现这是360加壳。
这是时候我们就要祭出脱壳神奇Youpk Youpk的操作文档具体可以看Youpk的github,再次感谢Youpk。我们在吧修复好的dex放在jadx中分析。修复的很给力,也可以看出源代码基本没有混淆,这就更利于我们的分析了。
破解VIP
- 利用Xposed/Frida破解 我们首先打开这个app,在主页点到一个人->进去,点击私信,发现要开通会员才可以呢。
这时候就可以 祭出搜索大法。打开jadx-全局搜索这个关键词“成为会员”。
这里可以看到有两个相同的。进入看看看。
这两处都是,我们分析一下这两处。
- 第一处
else if (this.isVip <= 1) {
new CommomDialog(this, R.style.dialog, true, "成为会员才能私聊哦!", new CommomDialog.OnCloseListener() {
/* class com.**.**.main.user.UserInfoActivity.AnonymousClass6 */
/* JADX WARN: Type inference failed for: r0v0, types: [android.content.Context, com.**.**.main.user.UserInfoActivity] */
@Override // com.**.**.widget.dialog.CommomDialog.OnCloseListener
public void onClick(Dialog dialog, boolean z) {
if (z) {
UserInfoActivity.this.startActivity(new Intent((Context) UserInfoActivity.this, MembersActivity.class));
dialog.dismiss();
}
}
}).setTitle("温馨提示").setPositiveButton("开通会员").show();
return;
else if 里面的this.isVip
只有小于1才会进入,也就是提示让你开通会员。
- 第二处
if (this.isVip > 1) {
Intent intent = new Intent((Context) this, (Class<?>) ChatActivity.class);
intent.putExtra(UserCacheInfo.COLUMNNAME_USERIDIMID, this.user_id);
intent.putExtra("userId", this.user_imid);
startActivity(intent);
return;
}
new CommomDialog(this, R.style.dialog, true, "成为会员才能私聊哦!", new CommomDialog.OnCloseListener() {
/* class com.**.**.main.user.UserInfoActivity.AnonymousClass8 */
/* JADX WARN: Type inference failed for: r0v0, types: [android.content.Context, com.**.**.main.user.UserInfoActivity] */
@Override // com.u**.**.widget.dialog.CommomDialog.OnCloseListener
public void onClick(Dialog dialog, boolean z) {
if (z) {
UserInfoActivity.this.startActivity(new Intent((Context) UserInfoActivity.this, MembersActivity.class));
dialog.dismiss();
}
}
}).setTitle("温馨提示").setPositiveButton("开通会员").show();
return;
}
return;
这里可以看到this.isVip
大于1的话就会提示“开通会员了呢”
- 最后 其实只要进入到
this.isVip
大于1那不就,,,,嘿嘿嘿 继续分析一下这个isVip是在哪里赋值呢。查找用例。。。。。
找到啦。。我们改下返回值
我们这里frida改一下。献上代码 这里还会有个小问题,就是这块类没在内存加载的时候会报错,这时候点下某人的主页就好了。
Java.perform(function () {
var pre = Java.use("com.***.***.utils.UncleSharedPreferences");
pre.getInt.overload('android.content.Context', 'java.lang.String').implementation = function (a1, a2) {
return 2;
}
})
看看效果。。。
发过去了。。。但是我们需要一直用的话就要一个xposed的插件。这里app采用的360加固,那我们就不能用常规的classloader进行hook,直接用360壳的classloader进行hook。
XposedHelpers.findAndHookMethod("com.stub.StubApp", loadPackageParam.classLoader, "attachBaseContext", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
Context context = (Context) param.args[0];
ClassLoader classLoader = context.getClassLoader();
classLoaders = classLoader;
XposedHelpers.findAndHookMethod("com.***.***.utils.UncleSharedPreferences", classLoader, "getInt", Context.class, String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//设置返回值为2
param.setResult(2);
}
});
XposedHelpers.findAndHookMethod("com.***.***.utils.DateUtil", classLoader, "getDayDiff", Date.class, Date.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(0L);
}
});
}
});
好了完美。。。私聊小姐姐(****)们
- 暗坑 最后发现他们有个体验会员,到期就是放弃(退出)
- 那还能咋办,继续分析 搜索大法
这里的if都是&&,一真则真,一假则假。前边几个看着不好搞,就dayDiff入手,改他返回值,小于3就行了。
代码就不放了。
- 小结 这个vip的破解还是很简单的那种。。。这块其实可以通过拦截请求的方式更改数据包,将vip字段设置一下,没具体分析。
聊天IM&&半自动im机器人
根据com.hyphenate.chat.EMMessage可以得到这是环信的im聊天,那就很简单了。两种方式实现发消息:
- 1.使用环信sdk,逆向该app的inti环境的一些配置信息,完成发消息。
- 2使用app本身的消息发送方法,用frida或者xposed主动调用。这里由于没有pythonSdk,所以采用第二种。
分析消息发送
这里我们上ddms和环信的文档,分析它的调用情况。文档如下
发送文本消息
//创建一条文本消息,content为消息文字内容,toChatUsername为对方用户或者群聊的id,后文皆是如此
EMMessage message = EMMessage.createTxtSendMessage(content, toChatUsername);
//如果是群聊,设置chattype,默认是单聊
if (chatType == CHATTYPE_GROUP)
message.setChatType(ChatType.GroupChat);
//发送消息
EMClient.getInstance().chatManager().sendMessage(message);
ddms如下
打开jadx分析这个类代码,可以看到如下
发送消息时候,创建了EMMessage.createTxtSendMessage,需要发送文本和对方的id。下面的代码是发送文本信息的
if (this.chatFragmentHelper != null) {
this.chatFragmentHelper.onSetMessageAttributes(eMMessage);
}
if (this.chatType == 2) {
eMMessage.setChatType(EMMessage.ChatType.GroupChat);
} else if (this.chatType == 3) {
eMMessage.setChatType(EMMessage.ChatType.ChatRoom);
}
EaseUser userInfo = EaseUserUtils.getUserInfo(eMMessage.getFrom(), UncleSharedPreferences.getString(SZApplication.getContext(), UncleSharedPreferences.SP_UID));
eMMessage.setAttribute("avatar", userInfo.getAvatar());
eMMessage.setAttribute("gender", UncleSharedPreferences.getString(SZApplication.getContext(), UncleSharedPreferences.SP_USER_SEX));
eMMessage.setAttribute("name", userInfo.getNickname());
eMMessage.setAttribute("token", UncleSharedPreferences.getString(SZApplication.getContext(), UncleSharedPreferences.SP_UID));
if (UserCacheManager.getImidFromCache(this.toChatUsername) != null) {
eMMessage.setAttribute("tokenTo", UserCacheManager.getImidFromCache(this.toChatUsername).getUserId());
} else {
eMMessage.setAttribute("tokenTo", "");
}
eMMessage.setAttribute("nameTo", eMMessage.getTo());
eMMessage.setAttribute("avatarTo", UncleSharedPreferences.getString(SZApplication.getContext(), UncleSharedPreferences.SP_TO_USER_AVATAR));
EMClient.getInstance().chatManager().saveMessage(eMMessage);
知道上边的我们就可以用frida玩玩。
var to_user_id = '1000477560';
var content = '我是一个机器人你信吗';
var uid = '1000511189';
var EMMessage = Java.use("com.hyphenate.chat.EMMessage");
var eMMessage = EMMessage.createTxtSendMessage(content, to_user_id);
eMMessage.setAttribute("avatar", "http://***/android/pic/1591284964")//头像
eMMessage.setAttribute("gender", "1")
eMMessage.setAttribute("name", "看123了看刻录机")
eMMessage.setAttribute("token", uid)//自己uid
eMMessage.setAttribute("nameTo", to_user_id)//对方imid
eMMessage.setAttribute("avatarTo", "")
eMMessage.setAttribute("tokenTo", to_user_id)
var EMClient = Java.use("com.hyphenate.chat.EMClient");
EMClient.getInstance().chatManager().saveMessage(eMMessage)
发送之后,需要点进去聊天界面,才会发送过去,并且如果这个app没有缓存这个用户信息,就会闪退。
解决闪退
继续ddms,点击私信。
搜索startAc,可以看到在UserInfoActivity下
分析到如下代码,可以很清楚到看到,这里吧用户信息存起来了。然后启动聊天的tActivity。
UserCacheManager.save(this.user_id, this.user_imid, this.nickname, this.avatar);
EaseUser easeUser = new EaseUser(this.user_imid);
easeUser.setAvatar(this.avatar);
easeUser.setNickname(this.nickname);
if (this.isVip > 1) {
Intent intent = new Intent((Context) this, (Class<?>) ChatActivity.class);
intent.putExtra(UserCacheInfo.COLUMNNAME_USERIDIMID, this.user_id);
intent.putExtra("userId", this.user_imid);
startActivity(intent);
return;
}
这次用xposed实现发送消息和启动ui。
Class EMMessage = XposedHelpers.findClass("com.hyphenate.chat.EMMessage", classLoaders);
Object eMMessage = XposedHelpers.callStaticMethod(EMMessage, "createTxtSendMessage", content, to_user_id);
XposedHelpers.callMethod(eMMessage, "setAttribute", "avatar", avatar);
XposedHelpers.callMethod(eMMessage, "setAttribute", "gender", gender);
XposedHelpers.callMethod(eMMessage, "setAttribute", "name", name);
XposedHelpers.callMethod(eMMessage, "setAttribute", "token", token);
XposedHelpers.callMethod(eMMessage, "setAttribute", "nameTo", nameTo);
XposedHelpers.callMethod(eMMessage, "setAttribute", "avatarTo", avatarTo);
XposedHelpers.callMethod(eMMessage, "setAttribute", "tokenTo", tokenTo);
Class EMClient = XposedHelpers.findClass("com.hyphenate.chat.EMClient", classLoaders);
Object getInstance = XposedHelpers.callStaticMethod(EMClient, "getInstance");
Object chatManager = XposedHelpers.callMethod(getInstance, "chatManager");
XposedHelpers.callMethod(chatManager, "saveMessage", eMMessage);
Class ChatActivity = XposedHelpers.findClass("com.***.***.main.im.ChatActivity", classLoaders);
Class UserCacheManager = XposedHelpers.findClass("com.***.***.main.im.cache.UserCacheManager", classLoaders);
XposedHelpers.callStaticMethod(UserCacheManager, "save", to_user_id, to_user_id, to_user_id, avatar);
Class EaseUser = XposedHelpers.findClass("com.hyphenate.easeui.domain.EaseUser", classLoaders);
Object easeUsernewInstance = XposedHelpers.newInstance(EaseUser, to_user_id);
XposedHelpers.callMethod(easeUsernewInstance, "setNickname", to_user_id);
XposedHelpers.callMethod(easeUsernewInstance, "setAvatar", avatar);
Intent intentChat = new Intent(contexts, ChatActivity);
intentChat.putExtra("userImId", to_user_id);
intentChat.putExtra("userId", to_user_id);
contexts.startActivity(intentChat);
这就基本完成了。
效果展示
最后借助NanoHTTPD做了web接口。看下效果
gif传不上来,戳原文看效果吧。
总结
这个app比较简单,虽然360加壳,但是通过youpk脱壳之后,发现基本没混淆。文中是借助ddms分析调用情况。这个可以继续扩展到,hook接收消息,收到消息之后,自动回复信息。接口在com...SZHelper$9.onMessageReceived上,以后有时间再分析写出来。
参考资料
[1]Youpk: https://github.com/Youlor/Youpk
[2]环信IM文档: http://docs-im.easemob.com/im/android/basics/message
- Selenium2+python自动化47-判断弹出框存在(alert_is_present)
- Free Pascal初次体验(有亮点哦)
- HDU 1312 Red and Black(DFS,板子题,详解,零基础教你代码实现DFS)
- Selenium2+python自动化48-登录方法(参数化)
- 51Nod 1003 阶乘后面0的数量(数学,思维题)
- 如何查看某个用户指定时间段的ABAP开发记录
- Selenium2+python自动化49-判断文本(text_to_be_present_in_element)
- 洛谷 P1876 开灯(思维,枚举,规律题)
- 线性回归:简单线性回归详解
- Codeforces 789A Anastasia and pebbles(数学,思维题)
- hihoCoder #1082 : 然而沼跃鱼早就看穿了一切(字符串处理)
- 51Nod 1182 完美字符串(字符串处理 贪心 Facebook Hacker Cup选拔)
- 51Nod 1080 两个数的平方和(数论,经典题)
- Selenium3+python自动化50-环境搭建(firefox)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- python实现单例模式的5种方法
- java序列化和序列化ID的作用
- python函数式编程
- 让Vim打造成强大的IDE,附_vimrc的配置和使用
- python 中面向切面编程AOP和装饰器
- HashMap&ConcurrentHashMap&HashTable
- python中的垃圾回收机制
- python中值传递还是引用传递?
- 基于Docker+Jenkins+Git的集成开发环境搭建
- python 函数的本质理解
- centOS(离线) off-line install docker-ce
- Java 工厂 Simple Factory&Factory&Abstract Factory
- python 性能的优化
- python中列表的常见操作
- Aop 源码解读