背景:钉钉开启课堂模式上课时,不能够批量邀请用户上台发言(下图所示),只能一个个点“邀请上台”按钮,给上课老师带来了极大的烦恼,为了优化用户体验,请给钉钉加上一键邀请学生上台发言的功能。
目标任务:完成钉钉课堂一键邀请上台功能、自动邀请上台功能
布置人:钱林松老师
作者:Cr39班 0xc5
软件版本:钉钉PC版本:6.0.26-Release.9039987
调试工具:x64Dbg Cheat Engine 7.0
已知信息:课堂直播的进程名:tblive.exe,tvlive.exe是钉钉主进程DingTalk.exe的子进程。
开启在线课堂,并使用x64dbg对tblive.exe进行附加,发现tblive.exe运行过程中会输出许多调试字符串,判定为程序执行过程中输出的调试信息。
笔者原先思路是对发包函数(sendto,end,WSASend,WSASendTo)下断点通过栈回溯找到关键CALL,现在转变思路,分析程序输出的调试信息,试图寻找关键的位置
笔者一一调用上课及邀请同学上台等功能,分析输出的调试字符串,发现了可疑的信息
重点关注这一条调试信息
此条日志输出后,继续出现了下列添加、移除成员的调试信息
根据上述添加成员、移除成员的调试信息,笔者猜测,这可能是邀请成员上台的相关内容,大胆猜测classroom_id为教室的门牌号,uids为邀请成员的编号。合理猜测,钉钉是通过SendMsg的方式将classroom_id及uids转发到server,由server根据MsgNum统一处理消息。
因此,全局搜索字符串“SendMsg”,搜索到如下位置
对两处下软件断点,再次点击邀请上台,第二处断点成功断下。
对上述反汇编代码进行分析,call <classroom.blog>是输出调式信息的函数,其参数分别是 eax,ecx,esi,依次对应Retcode,msg,msgNumber,eax作为Retcode来源于call edx 这一间接调用中,猜测可能是发包函数,在call edx 处下软件断点。重新邀请成员上台,断点命中如下图所示!
笔者对该CALL参数进行分析,对参数分析如下
寄存器来源:
1.eax:指向数据包ANSI字符串的指针。
2.edi:固定值0x7cf
3.edx:调用的发包函数地址,在ipcclient.dll 0x2f20偏移处,
4.ecx:通过使用CE的指针扫描器进行扫描,得出其值是[[classroom.dll+0x53fc04]+4]
笔者通过之前的分析,已经明确该调用该CALL所需的参数,针对邀请数据包的构造,还需要两个参数classroom_ID 及User_ID ,即可构造数据包
笔者通过不断的查看调用堆栈进行分析,最后找到下面这个位置
继续分析,ecx来源于上一层调用后的eax的值,即call classroom.2766098的返回值,通过分析,最终确认 ClassRoom_ID 来源于 [classroom.dll + 0x542fB4]+c
笔者通过栈回溯发现User_ID在动态存储的,只要台下学生列表刷新,其存储位置就会被释放然后重新分配内存存储,给获取带来一定的困难,因此笔者另辟蹊径,重新从日志寻找线索。
笔者继续从输出的调试信息入手,经过反复测试,发现了上面的这一条关键性的日志。通过调试信息的内容推测是台下在线用户的信息,成功获取User_ID。
至此,调用发包函数Call edx的参数已经准备完毕,笔者使用CE的自动汇编功能进行测试。
通过动态获取的Classroom_id、User_ID 构造邀请数据包,并使用CE在tblive.exe动态分配128字节内存,写入数据包信息
然后使用CE自动汇编功能调用邀请call
执行自动汇编脚本,成功邀请用户上台如下图:
由之前的分析可知,若要实现自动邀请/一键邀请用户上台发言的功能,则必不可少的就是邀请的用户的User_ID列表。
笔者通过不断的进入课堂,发现每当新用户进入时,tblive.exe会输出下列表示信息
调试信息中就包含有User_ID等信息。
笔者再次通过搜索字符串(LOW,但是在这里有效)的方法,定位输出调试字符串的位置。
通过对此处进行InlineHook,即可获得User_ID,具体代码就不贴了,思路如下:
1.注入DLL,获取调用邀请CALL的所有参数信息
2.在tblive.exe进程内申请合适的内存用于存放邀请数据包的信息及执行邀请CALL的反汇编代码,并将相关反汇编代码拷贝到申请的内存中。
3.在上图处通过inlinehook获取user_id;存放到数据结构中;并使用Classroom_ID及User_ID构造邀请数据包,将其作为参数传入构造好的邀请CALL中,即可实现自动邀请用户上台发言的功能。
4.一键邀请上台功能的实现:在步骤3中,已存好每次进入课堂的User_ID,只需遍历该数据结构,构造邀请数据包,不断调用邀请CALL即可实现一键上台。
/
/
下面是程序输出的部分调试信息。
调试字符串:
"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."
/
/
下面是程序输出的部分调试信息。
调试字符串:
"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."
调试字符串:
"SendMsg: 1405, {"
classroom_id
":"
61489d302bb1d501588adde0
","
uids
":[2120974346]}, retCode:1"
调试字符串:
"SendMsg: 1405, {"
classroom_id
":"
61489d302bb1d501588adde0
","
uids
":[2120974346]}, retCode:1"
调试字符串:
"RemoveJoinedMember, uid=2120974346"
调试字符串:
"RemoveJoinedMember, uid=2120974346 not exist"
调试字符串:
"AddUnjoinedMember, uid=2120974346"
调试字符串:
"AddUnjoinedMember, uid=2120974346 already exist"
调试字符串:
"RemoveJoinedMember, uid=2120974346"
调试字符串:
"RemoveJoinedMember, uid=2120974346 not exist"
调试字符串:
"AddUnjoinedMember, uid=2120974346"
调试字符串:
"AddUnjoinedMember, uid=2120974346 already exist"
;以下是第二处断点的上下文反汇编
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
;以下是第二处断点的上下文反汇编
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
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-9-25 22:28
被0xC5编辑
,原因: