用 Xposed Hook 网校软件"无限宝"

 

先贴出 Github 地址: https://github.com/lyc8503/VizpowerHook.

众所周知, 因为一只蝙蝠, 暑假没有了, 寒假像滚雪球似的越来越长, (网课却照上不误).

我们学校使用了一款名叫 无限宝 的网课软件, 软件功能主要就是课程直播, 附带(?)了一些点名签到, 自动锁屏, 认真度统计**(会直接截取电脑屏幕)**. 用了几天之后, 想添加一个自动进入课堂, 自动签到, 外加自动录像保存的功能.

开始打算逆向它的协议, 结果它的协议是基于TCP的, 用的Java nio, 整个通讯协议还是挺复杂的, 感觉重写协议栈耗时过久代价太大, 便换了个思路, 打算用客户端的逆向与修改实现功能.

以前到现在用的语言一直是 Java, 安卓开发也学过, 所以客户端的逆向难度应该也不是很大…(flag高高挂起x

结果刚刚反编译客户端就遇到了麻烦: 他喵的这客户端加固过了 !!

一看 qihoo, 是360的壳, 网上找找工具应该能脱下来, 利用 Java 的反射或直接 Hook 系统的载入 Dex 文件代码也都能很轻易的脱壳, 就是费点事, 不再多说.

很快拿到真正的 Dex 文件, 二话不说直接丢进 Jadx 反编译. 发现这段代码没有混淆.(??)

反编译过后的源代码反编译过后的源代码

以逆向登陆协议为例:

直接抓包找到相关接口…通过 Fiddler 抓到相关包, 分析后用 python requests 库可以写成这样的代码:

1
2
3
4
5
6
7
8
9
r = requests.get("http://" + prefix + ".kehou.com/courseList.action", params={
"uid": user_name,
"pwd": pwd,
"pwd2": pwd2,
"salt": salt,
"callfrom": "vpAndroid",
"mac": "00:00:00:00:00:00",
"needReplayInfo": "CourseListAndReplayCourseList"
})

其中 salt 是当前的 unix 时间戳, pwd 和 pwd2 是 MD5 加密过后的密码.

1
2
String pwd = MD5.stringMD5(UserName + strPassword + strSalt + "WINUPON").toLowerCase();
String pwd2 = MD5.stringMD5(UserName + MD5.stringMD5(strPassword).toLowerCase() + strSalt + "WINUPON").toLowerCase();

有了登陆协议可以轻松获取模拟客户端的登陆… 返回数据样例如下(是一个ini文件):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
[mt1]
MeetId=55172457
timesId=13
Meeting-Subject=高一学年测试学员是否正常登陆课堂
MeetCurrTime=2020-02-29 13:27:52
Meeting-Duration=06:00:00-23:00:00
EastimateTime=1020
MeetStartTime=2020-02-29 06:00:00.000
Meeting-Chairman=主讲教师
Meeting-Project=[REMOVED]
Meeting-BeforeTime=15
userId=72949
NickName=[REMOVED]
Course-Big-PictureUrl=http://cnkehou.kehou.com/vipkehou/upload/course/12/2020/02/18/55172457_1582010896851_big.jpg
Course-Total-time=44
WinAppTitle=[REMOVED]
ProjectName=[REMOVED]
NeedSSR=1
ServerIP=hzlawxlogin.wanpeng.net;hzlawxlogin2.wanpeng.net
Port=7000
AutoRecordPrompt=0
Meeting-AddTime=15
ClientType=1
ProxyAllocType=0
MultiMeeting=0
listenType=0
MeetingQuitURL=
PresidentKey2=f0cbeb337a58fd8db2849698627f1553
VerifyKey=cf10bc1b4c7f592948335a4a2c1d8d67
signupCount=0
TeacherOverviewURL=http://sdfz.vip.kehou.com/teacherInfo.htm?userId=67934
StudentDetailURL=http://sdfz.vip.kehou.com/studentInfo.htm?
SnapUploadURL=http://vpbridge.vip.kehou.com/dataii/wxb_snapshot_upload.do
TestResultURL=
VoteResultURL=
RestVodURL=http://cnkehou.kehou.com/vipkehou/sysfile/video/ybjc.swf
QRCodeBaseURL=
ClassNoteURL=http://vpbridge.vip.kehou.com/dataii/class_note_interaction.do?
StuSchool=[REMOVED]
StuClass=[REMOVED]
StuPhone=
CameraRemind=0
CameraSnap=0
NDConf=100;2000
ShowUserCount=0
ClassAutoLock=0
IPVCamera=0
VideoQualityLevel=1
AllowHLS=0
NetDiskProtocol=4
NetDiskUploadURL=0:#:cnkehou:#:vipkehou/uploads/Presenter/55172457_{timesid}/
NetDiskNotifyURL=[REMOVED]
NetDiskUserName=[REMOVED]
NetDiskUserPasswd=[REMOVED]
EvaluateURL=https://sdfz.vip.kehou.com/wxbCourse/courseComment-openDiv.htm?userId=72949&courseId=55172457&courseSeq=13&mil=1582954072348&sign=552cc56b59c618ecd3944bf2cdee4caa&source=1&
DocURL=http://vpbridge.vip.kehou.com/dataii/wxb_doc_interaction.do?
GreenScreenURL=http://sdfz.vip.kehou.com/background/queryBackgroundPic.htm?agencyId=12
FeedbackURL=http://sdfz.vip.kehou.com/wxbSuggestion/wxbSuggestion-openDiv.htm?userId=72949&mil=1582954072348&sign=d87f8ef2f2baacd01e29ad6af5720822
MultiVideoChannels=0
SensitiveWordsURL=http://cnkehou.kehou.com/vipkehou/upload/filterWord/2020/02/20/sensitiveWords_1582191548329.txt
EditTestStdAns=0

从返回数据中可以获取下一步登陆服务器的 IP 地址和端口… 但是接下来的通讯都使用了 TCP 协议, 逆向分析相对比较复杂… 就换了一个角度: 使用 Xposed 框架直接 Hook 软件本身获取或修改相关数据.

以强制打开公聊为例: 搜索”私聊”字样, 找到如下代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final void clickSwitchChatTo() {
if (ChatMgr.getInstance().canSendChatPub() && ChatMgr.getInstance().canSendChatPriv()) {
this.m_bChatToPub = !this.m_bChatToPub;
checkEnableInput();
if (this.m_bChatToPub) {
iMeetingApp.getInstance().showAppTips("切换到公聊");
} else {
iMeetingApp.getInstance().showAppTips("切换到与老师私聊");
}
} else if (this.m_bChatToPub) {
iMeetingApp.getInstance().showAppTips("当前仅限进行公聊!");
} else {
iMeetingApp.getInstance().showAppTips("当前仅限与老师进行私聊!");
}
}

很显然 ChatMgr.getInstance().canSendChatPub() 方法是判断是否能进行公聊的方法.

直接使用 Xposed Hook 方法, 直接返回 true.

代码如下(前面绕过 360 加固部分的代码太长就不发了x):

1
2
3
4
5
6
7
public class PubChatHook extends XC_MethodHook {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
param.setResult(true);
}
}

再次登陆课堂, 发现可以在老师没有打开公聊权限的时候发送公聊…

效果图效果图

以此类推, 可以实现很多更多的功能, 详见 Github: https://github.com/lyc8503/VizpowerHook.

至于自动录像课程… 有点懒暂时不想做了(反而开始了写外挂了(雾)), 反正丢在虚拟机里开个 OBS 就可以录制了.

总结: 学到了挺多知识的, 踩坑肯定会有, 另开文章再写, 进一步熟悉了Android开发(终于不是抄抄样例程序混日子了x), 顺便找到这个相对简单的任务练练手, 学会了Xposed API, 不知道 接下来 Hook QQ & 微信 有没有希望.(或者可能选用他人现成的 QQ & 微信 框架不要重造轮子更好…?)

(其实本来学 Xposed 真的只是想写一个 QQ 机器人没想到副产物就是这个 Vizpower Hook了.)

2020.03.12 补充: 后来反编译的代码被官方发现了, 然后就在 Github 上面把无限宝本体的代码下架了, 那些漏洞官方应该也会修, 但官方也没要求我删除这个项目, 留下这个 Github 项目留念也不错, 恰好我们学校因为大课堂效果不好, 在3月中旬之后也不使用无限宝上课了, (也算是有一个比较完美的结局?), 所以就这样啦~

2020.08.13更新: 写了无限宝作业爬虫之后发现现在使用 AES 加密来加密登陆参数. 逆向思路类似就不再写文章了.

本文采用 CC BY-NC-SA 4.0 许可协议发布.

作者: lyc8503, 文章链接: https://blog.lyc8503.net/post/vizpower-hook/
如果本文给你带来了帮助或让你觉得有趣, 可以考虑赞助我¬_¬