首页
论坛
课程
招聘
[原创]对钉钉课堂邀请用户上台发言功能的逆向及成果利用
2021-9-25 18:11 25171

[原创]对钉钉课堂邀请用户上台发言功能的逆向及成果利用

2021-9-25 18:11
25171

任务说明

背景:钉钉开启课堂模式上课时,不能够批量邀请用户上台发言(下图所示),只能一个个点“邀请上台”按钮,给上课老师带来了极大的烦恼,为了优化用户体验,请给钉钉加上一键邀请学生上台发言的功能。

 

目标任务:完成钉钉课堂一键邀请上台功能、自动邀请上台功能

 

布置人:钱林松老师

 

作者:Cr39班 0xc5

分析说明

软件版本:钉钉PC版本:6.0.26-Release.9039987

 

调试工具:x64Dbg Cheat Engine 7.0

分析过程

1.对邀请上台CALL的定位及分析

  已知信息:课堂直播的进程名:tblive.exetvlive.exe是钉钉主进程DingTalk.exe的子进程。

 

  开启在线课堂,并使用x64dbgtblive.exe进行附加,发现tblive.exe运行过程中会输出许多调试字符串,判定为程序执行过程中输出的调试信息。

 

  笔者原先思路是对发包函数(sendto,end,WSASend,WSASendTo)下断点通过栈回溯找到关键CALL,现在转变思路,分析程序输出的调试信息,试图寻找关键的位置

 

  笔者一一调用上课及邀请同学上台等功能,分析输出的调试字符串,发现了可疑的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//下面是程序输出的部分调试信息。
调试字符串: "OnEvent code:11200, module:audio_energy, desc:, attribute:{"duration":5876,"playCallbacks":320,"playDuration":5876,"playLevel":1,"playMs":3199,"playRate":26140,"playSamples":153600,"recCallbacks":322,"recDuration":5876,"recLevel":99,"recMs":5897,"recRate":26304,"recSamples":154560}"
调试字符串: "OnEvent code:14002, module:ice_connection, desc:ice_network_interface_detected, attribute:{"Cellular":false,"Wifi":false,"any":false,"many":false,"relay":false,"sessionid":"315298512524207800","vpn":false}"
调试字符串: "OnEvent code:14002, module:ice_connection, desc:ice_network_interface_detected, attribute:{"Cellular":false,"Wifi":false,"any":false,"many":false,"relay":false,"sessionid":"774674212781047000","vpn":false}"
调试字符串: "SendMsg: 1405, {"classroom_id":"61489d302bb1d501588adde0","uids":[2120974346]}, retCode:1"
调试字符串: "conf OnMcsEvent  code:14007, msg:ice_connection_stats"
调试字符串: "conf OnMcsEvent  code:14007, msg:ice_connection_stats"
调试字符串: "conf OnMcsEvent  code:11200, msg:"
调试字符串: "conf OnMcsEvent  code:14002, msg:ice_network_interface_detected"
调试字符串: "conf OnMcsEvent  code:14002, msg:ice_network_interface_detected"
调试字符串: "OnAppInactive"
调试字符串: "OnChannelMsgFromServer: 1999, {"cmd":101,"body":"{\"conference_id\":\"61489d302bb1d501588adde0\",\"user_id\":\"2120974346\",\"nick\":\"sun\",\"ext_info\":\"\",\"role\":0,\"attend_status\":2,\"sub_status\":0,\"reject_description\":\"\",\"camera_status\":0,\"mic_status\":0,\"device_id\":\"X6tfdhCQZrMDAMVSGCCYhOPr\",\"start_timestamp\":0,\"record_status\":0,\"uid_domain\":\"DingTalk\",\"pdp_code\":0,\"protocol\":\"webrtc\",\"device_type\":\"\",\"sip_uid\":\"\",\"sip_pid\":\"\"}"}"
调试字符串: "OnIpcConfMemberChanged, {"conference_id":"61489d302bb1d501588adde0","user_id":"2120974346","nick":"sun","ext_info":"","role":0,"attend_status":2,"sub_status":0,"reject_description":"","camera_status":0,"mic_status":0,"device_id":"X6tfdhCQZrMDAMVSGCCYhOPr","start_timestamp":0,"record_status":0,"uid_domain":"DingTalk","pdp_code":0,"protocol":"webrtc","device_type":"","sip_uid":"","sip_pid":""}"
调试字符串: "RemoveJoinedMember, uid=2120974346"
调试字符串: "RemoveJoinedMember, uid=2120974346 not exist"
调试字符串: "AddUnjoinedMember, uid=2120974346"
调试字符串: "AddUnjoinedMember, uid=2120974346 already exist"
调试字符串: "ConfMgr::OnParticipantJoined, origin:61489efa2bb1d501588d95e0, uid=2120974346"
调试字符串: "ConfMgr::OnParticipantJoined no remote stream to add to map, participant_id:61489efa2bb1d501588d95e0, uid:2120974346"
调试字符串: "INFO: add observer for participant:61489efa2bb1d501588d95e0"
调试字符串: "Invalid parameter passed to C runtime function."

重点关注这一条调试信息

1
调试字符串: "SendMsg: 1405, {"classroom_id":"61489d302bb1d501588adde0","uids":[2120974346]}, retCode:1"

此条日志输出后,继续出现了下列添加、移除成员的调试信息

1
2
3
4
调试字符串: "RemoveJoinedMember, uid=2120974346"
调试字符串: "RemoveJoinedMember, uid=2120974346 not exist"
调试字符串: "AddUnjoinedMember, uid=2120974346"
调试字符串: "AddUnjoinedMember, uid=2120974346 already exist"

  根据上述添加成员、移除成员的调试信息,笔者猜测,这可能是邀请成员上台的相关内容,大胆猜测classroom_id为教室的门牌号,uids为邀请成员的编号。合理猜测,钉钉是通过SendMsg的方式将classroom_iduids转发到server,由server根据MsgNum统一处理消息。

 

​ 因此,全局搜索字符串“SendMsg”,搜索到如下位置

 

​ 对两处下软件断点,再次点击邀请上台,第二处断点成功断下。

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
;以下是第二处断点的上下文反汇编
lea ecx,dword ptr ss:[ebp-44]
mov byte ptr ss:[ebp-4],1
call classroom.2CC16FE
mov edi,dword ptr ss:[ebp-60]
mov ecx,dword ptr ss:[ebp-64]
cmp dword ptr ss:[ebp-14],10
mov ecx,dword ptr ds:[ecx+4]
mov eax,dword ptr ds:[ecx]
mov edx,dword ptr ds:[eax]
lea eax,dword ptr ss:[ebp-28]
cmovae eax,dword ptr ss:[ebp-28]
push eax
push edi
call edx
cmp esi,1FE
je classroom.2D59C47
cmp esi,34F
je classroom.2D59C47
cmp esi,354
je classroom.2D59C47
cmp esi,7D0
je classroom.2D59C47
cmp esi,4B1
je classroom.2D59C47
cmp dword ptr ss:[ebp+20],10
lea ecx,dword ptr ss:[ebp+C]
movzx eax,al
cmovae ecx,dword ptr ss:[ebp+C]
push eax
push ecx
push esi
push classroom.2FDCC30            ;SendMsg字符串
push 12C
call <classroom.blog>            ;输出日志函数
add esp,14

​ 对上述反汇编代码进行分析,call <classroom.blog>是输出调式信息的函数,其参数分别是 eax,ecx,esi,依次对应Retcode,msg,msgNumbereax作为Retcode来源于call edx 这一间接调用中,猜测可能是发包函数,在call edx 处下软件断点。重新邀请成员上台,断点命中如下图所示!

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
;函数头部
push ebp
mov ebp,esp
sub esp,2C
mov eax,dword ptr ds:[B0D1280]
xor eax,ebp
mov dword ptr ss:[ebp-4],eax
push ebx                       
push esi
push edi                        ;保存了ebx esi edi
mov edi,dword ptr ss:[ebp+C]
xor ebx,ebx                        ;ebx清零
push 20
mov dword ptr ss:[ebp-24],ecx    ;ecx直接使用
mov dword ptr ss:[ebp-20],ebx
call ipcclient.B07E8B0
mov esi,eax
add esp,4
test esi,esi
je ipcclient.B072FCB
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;函数尾部
mov ecx,dword ptr ss:[ebp-4]
pop edi
pop esi
xor ecx,ebp
pop ebx
call ipcclient.B0A631A
mov esp,ebp
pop ebp
ret 8                        ;ret 8 是push两个参数平栈

笔者对该CALL参数进行分析,对参数分析如下

1
2
3
4
eax == {"cmd":1405,"body":"{\"classroom_id\":\"6141e18849499301574bc020\",\"uids\":[2120974346]}"}
push eax        ;ANSI字符串,发送的数据包
push edi        ;edi,ecx不知道是什么鬼东西,反正有用
call edx        ;edx发送数据包函数的地址

寄存器来源:

 

​ 1.eax:指向数据包ANSI字符串的指针。

 

​ 2.edi:固定值0x7cf

 

​ 3.edx:调用的发包函数地址,在ipcclient.dll 0x2f20偏移处,

 

​ 4.ecx:通过使用CE的指针扫描器进行扫描,得出其值是[[classroom.dll+0x53fc04]+4]

调用CALL验证猜想

​ 笔者通过之前的分析,已经明确该调用该CALL所需的参数,针对邀请数据包的构造,还需要两个参数classroom_IDUser_ID ,即可构造数据包

寻找ClassRoom_ID:

​ 笔者通过不断的查看调用堆栈进行分析,最后找到下面这个位置

1
2
3
4
5
6
7
8
9
push ebp
mov ebp,esp
lea eax,dword ptr ds:[ecx+6C]    ;[ecx+6c] == &szClassroom_ID
mov ecx,dword ptr ss:[ebp+8]
push eax
call classroom.27E3F71
mov eax,dword ptr ss:[ebp+8]
pop ebp
ret 4

​ 继续分析,ecx来源于上一层调用后的eax的值,即call classroom.2766098的返回值,通过分析,最终确认 ClassRoom_ID 来源于 [classroom.dll + 0x542fB4]+c

寻找User_ID

​ 笔者通过栈回溯发现User_ID在动态存储的,只要台下学生列表刷新,其存储位置就会被释放然后重新分配内存存储,给获取带来一定的困难,因此笔者另辟蹊径,重新从日志寻找线索。

1
调试字符串: "CConferenceManager::OnIpcLiveOnlineUsers: {"user_id":"2120974346","nick":"sun(sun)","header_image":"","header_image_cached":"","org":"","support_hand":true,"has_more":false}"

​ 笔者继续从输出的调试信息入手,经过反复测试,发现了上面的这一条关键性的日志。通过调试信息的内容推测是台下在线用户的信息,成功获取User_ID

调用

​ 至此,调用发包函数Call edx的参数已经准备完毕,笔者使用CE的自动汇编功能进行测试。

 

​ 通过动态获取的Classroom_idUser_ID 构造邀请数据包,并使用CE在tblive.exe动态分配128字节内存,写入数据包信息

1
{"cmd":1405,"body":"{\"classroom_id\":\"614c89ec49719f0158adf290\",\"uids\":[2120974346]}"}

 

​ 然后使用CE自动汇编功能调用邀请call

 

 

​ 执行自动汇编脚本,成功邀请用户上台如下图:

2.对邀请CALL的利用(一键邀请上台功能、自动邀请上台功能)

​ 由之前的分析可知,若要实现自动邀请/一键邀请用户上台发言的功能,则必不可少的就是邀请的用户的User_ID列表。

 

​ 笔者通过不断的进入课堂,发现每当新用户进入时,tblive.exe会输出下列表示信息

1
调试字符串: "CConferenceManager::OnIpcLiveOnlineUsers: {"user_id":"2120974346","nick":"sun(sun)","header_image":"","header_image_cached":"","org":"","support_hand":true,"has_more":false}"

​ 调试信息中就包含有User_ID等信息。

 

​ 笔者再次通过搜索字符串(LOW,但是在这里有效)的方法,定位输出调试字符串的位置。

 

​ 通过对此处进行InlineHook,即可获得User_ID,具体代码就不贴了,思路如下:

 

1.注入DLL,获取调用邀请CALL的所有参数信息

 

2.在tblive.exe进程内申请合适的内存用于存放邀请数据包的信息及执行邀请CALL的反汇编代码,并将相关反汇编代码拷贝到申请的内存中。

 

3.在上图处通过inlinehook获取user_id;存放到数据结构中;并使用Classroom_IDUser_ID构造邀请数据包,将其作为参数传入构造好的邀请CALL中,即可实现自动邀请用户上台发言的功能。

 

4.一键邀请上台功能的实现:在步骤3中,已存好每次进入课堂的User_ID,只需遍历该数据结构,构造邀请数据包,不断调用邀请CALL即可实现一键上台。

Ps:文章到此结束,笔者只提供思路,所有软件成品内部使用


[公告] 欢迎大家踊跃尝试高研班11月试题,挑战自己的极限!

最后于 2021-9-25 22:28 被0xC5编辑 ,原因:
收藏
点赞10
打赏
分享
打赏 + 5.00
打赏次数 1 金额 + 5.00
 
赞赏  Imyang   +5.00 2021/09/26 感谢分享~
最新回复 (8)
雪    币: 2519
活跃值: 活跃值 (1167)
能力值: ( LV8,RANK:124 )
在线值:
发帖
回帖
粉丝
KuCha128 活跃值 1 2021-9-26 14:09
2
0
棒!
雪    币: 1554
活跃值: 活跃值 (1520)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
0xC5 活跃值 1 2021-9-26 16:34
3
0
KuCha128 棒!
感谢夸奖
雪    币: 344
活跃值: 活跃值 (755)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ogli324 活跃值 2021-9-26 16:57
4
0
伟大的逆向攻城狮, 官方不做的, 我们就自食其力。 彩~
雪    币: 869
活跃值: 活跃值 (563)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
0xNOPE 活跃值 2021-9-26 22:10
5
0
学习了
雪    币: 72
活跃值: 活跃值 (544)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
君君寒 活跃值 2021-9-27 09:29
6
0
很不错
雪    币: 215
活跃值: 活跃值 (389)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
尼古拉斯大拿 活跃值 2021-10-4 14:57
7
0
学习了
雪    币: 1729
活跃值: 活跃值 (520)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
miaostart 活跃值 2021-10-6 09:06
8
0
思路很清晰,分析很到位,学习了,感谢分享!
雪    币: 143
活跃值: 活跃值 (130)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
chengtao 活跃值 2021-10-6 14:11
9
0
逆向需要灵感及毅力
游客
登录 | 注册 方可回帖
返回