钉钉邀请上台功能分析

VSole2021-10-19 16:37:37

因为钉钉课堂不上台成员与上台成员延迟差距太大,而当人数过多时挨个邀请太费时间,又有部分成员有网络波动等原因导致需要反复邀请。所以为了解放老师上课的双手,这个产品就诞生了出来。

一、分析邀请call

1、首先分析钉钉的一键邀请是如何发送到服务器的。

2、首先x32dbg附加tblive.exe进程对所有发包函数进行下断,sendto,send,WSASend,WSASendTo。

3、经过反复测试,点击邀请按钮没有任何一个函数被断下。

4、而我们也知道tblive进程是由DingTalk主进程创建出来的,那么他们可能存在管道通讯由主进程进行发包。

5、所以附加DingTalk进程,并重复上述操作,在WSASend函数断了下来。

6、那么回溯到函数外层查看传参的数据。

7、休息时思考了一下为什么tblive进程中对发包函数下断无法断下邀请上台功能,而在钉钉主进程却可以。那么就验证了我们上面分析的子进程和父进程直接是含有通讯,假设教师创建课堂的时候课堂会保存一个类这个类包含了课堂id,台下学生链表等必要信息。当一个学员点开视频则加入台下学生链表中,当教师点击台下学生邀请上台时。那么tblive就会组装消息通过子父进程通讯给钉钉主进程, 由主进程和服务器进行通讯完成该功能。那么子父进程的通讯常见的也就几种,通过测试可以发现钉钉采用的管道通讯。

8、分析钉钉的子父进程通讯,首先下断WriteFile的时候发现断点一直来,看来一眼线程模块,发现有很多线程,那么我作为开发角度一定会把io相关操作使用多线程避免阻塞主线程从而影响上课的网速。所以我这里首先暂停所有线程保留主线程,同时在WriteFile函数下断,点击邀请按钮查看WriteFile函数是否断下成功,结果也是显而易见的,并不能断下来。后续通过折半查找法,定位到了钉钉的消息队列处理线程。

9、只保留主线程和消息队列线程,再次给WriteFile函数下断并且点击邀请按钮,可以看到相关的重要信息。

10、通过这个信息,我们可以很确定的判断出我们的假想成立。那么现在需要的工作就是通过回溯找到关键call。

11、从该信息我们可以得知,需要完成这个功能至少需要classroom_id和uids。通过栈回溯,发现了一处代码使用了HeapFree函数,于是在HeapFree上下断再次点击邀请按钮。

12、再次将栈窗口滚动查看,于是我们看到了uids和classroom_id分开存放的位置,直觉告诉我距离关键call已经很近了。

13、我们向上回溯后很清晰的看到了他所传入的两个call,一个为uids一个为classroom_id。

14、通过测试,我们发现这个call是可以用来邀请人员的,而他的id是一个结构体,包含了uids的长度,否则在组包的时候会被截断。通过函数的头和尾部,不难看出这是一个类对象的成员函数,而他的外部call只是给ecx进行了一次赋值,没有做多余的操作,而这个ecx恰巧是被邀请人的对象指针。通过替换ecx指针为不同人的话也可以办到邀请不同的人。他要比内部call更为简单,所以我们准备采用这个call作为邀请call。

二、分析创建成员指针

1、那么距离自动邀请,还差一个成员指针从何而来。通过分析钉钉的行为,我们得知一个信息就是,每当台下人员有所变动后所有的成员指针都会被释放掉并且重新new一片空间。所以只要我们找到了哪里对他们进行生成的函数,我们就可以通过hook得到最新的成员指针地址,所以对tblive的malloc函数下断。

2、而malloc函数一直来断点很影响我们的判断,而我看到了malloc的eax他有模块地址的信息,而我们也确切直到成员的函数指针在模块classroom.dll下面,所以对齐断点设置条件断点当eax的值在classroom.dll的范围内的时候再断下来。

3、我们现在将台下成员进行变动一下,malloc被断下来了,通过观察堆栈,看看是否有相关的敏感信息,F9了几下我们就看到了栈中的classroom_id和uids了。所以这个时候取消malloc的断点开始向上回溯,停到疑似创建成员指针的位置后下断重新变动台下成员,经过验证这个call则为创建成员指针。

4、那么已知条件已经具备,附上我的代码。通过劫持注入的方式可以当钉钉创建tblive的时候我的dll也就被加载了,同时也将附带上自动邀请的功能,可以让老师不用频繁的拉人上台了。注意劫持的dll是air2.dll,将原dll名称改为air2Org.dll即可。

三、源代码

#include "pch.h" #include <Windows.h>#pragma comment(linker,"/EXPORT:?air_roi_create@@YAHPAPAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z=air2Org.?air_roi_create@@YAHPAPAXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z")#pragma comment(linker,"/EXPORT:?air_roi_destroy@@YAHPAX@Z=air2Org.?air_roi_destroy@@YAHPAX@Z")#pragma comment(linker,"/EXPORT:?air_roi_detect@@YAHPAXPBEHHHW4air_pixel_format@@PAEHH@Z=air2Org.?air_roi_detect@@YAHPAXPBEHHHW4air_pixel_format@@PAEHH@Z")#pragma comment(linker,"/EXPORT:air_dl_segment_create=air2Org.air_dl_segment_create")#pragma comment(linker,"/EXPORT:air_dl_segment_destroy=air2Org.air_dl_segment_destroy")#pragma comment(linker,"/EXPORT:air_dl_segment_detect=air2Org.air_dl_segment_detect")#pragma comment(linker,"/EXPORT:air_dl_segment_get_insize=air2Org.air_dl_segment_get_insize")  // 创建成员指针偏移const DWORD g_dwCreateMemberOffset = 0x1E1523;const DWORD g_dwOrginCallOffset = 0x36BB;// 邀请call偏移const DWORD g_dwInviteCallOffset = 0x1974c0;  #define HOOK_LEN 5VOID SetHook(DWORD_PTR dwHookAddr, LPVOID dwPfnAddr, BYTE* btBackCode){    // 准备HOOK    BYTE btJmp[HOOK_LEN] = { 0xE9 ,0,0,0,0 };    *(DWORD*)&btJmp[1] = (DWORD)dwPfnAddr - dwHookAddr - HOOK_LEN;    // 获取自身进程句柄    HANDLE hProcess = GetCurrentProcess();    // 备份数据    if (!ReadProcessMemory(hProcess, (LPVOID)dwHookAddr, btBackCode, HOOK_LEN, NULL))    {        MessageBox(NULL, "HOOK失败", NULL, NULL);        return;    }    // 开始hook    if (!WriteProcessMemory(hProcess, (LPVOID)dwHookAddr, btJmp, HOOK_LEN, NULL))    {        MessageBox(NULL, "HOOK失败", NULL, NULL);        return;    }} VOID UnSetHook(DWORD_PTR dwHookAddr, BYTE* btBackCode){    // 获取自身进程句柄    HANDLE hProcess = GetCurrentProcess();    // 卸载hook    if (!WriteProcessMemory(hProcess, (LPVOID)dwHookAddr, btBackCode, HOOK_LEN, NULL))    {        MessageBox(NULL, "卸载HOOK失败", NULL, NULL);        return;    }} // 邀请原数据DWORD g_dwCreateMemberOrgCallAddr = NULL;DWORD g_dwCreateMemberAddr = NULL;BYTE g_btCreateMemberCode[5]{};DWORD g_dwCreateMemberCallback = NULL;DWORD g_dwInvateCallAddr = NULL;DWORD g_dwMemberPoint = NULL;void AutoInviteMember(){    __asm    {        mov ecx, g_dwMemberPoint        call g_dwInvateCallAddr    }    return;} _declspec(naked) void HookCreateMemberFunc(){    __asm     {        pushad        pushf        mov eax, dword ptr ss:[ebp - 0x48]        mov [g_dwMemberPoint], eax    }    AutoInviteMember();    __asm    {        popf        popad        call g_dwCreateMemberOrgCallAddr        jmp g_dwCreateMemberCallback    }} // 入口函数BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved){    if (dwReason == DLL_PROCESS_ATTACH)    {        // 初始化数据        DWORD dwClassroomHandle = (DWORD)GetModuleHandle("classroom.dll");        g_dwInvateCallAddr = dwClassroomHandle + g_dwInviteCallOffset;        g_dwCreateMemberAddr = dwClassroomHandle + g_dwCreateMemberOffset;        g_dwCreateMemberCallback = g_dwCreateMemberAddr + HOOK_LEN;        g_dwCreateMemberOrgCallAddr = dwClassroomHandle + g_dwOrginCallOffset;         // HOOK加入成员生成指针部分        SetHook(g_dwCreateMemberAddr, HookCreateMemberFunc, g_btCreateMemberCode);    }    else if (dwReason == DLL_PROCESS_DETACH)    {        UnSetHook(g_dwCreateMemberAddr, g_btCreateMemberCode);    }        return TRUE;}
指针功能分析
本作品采用《CC 协议》,转载必须注明作者和本文链接
那么tblive就会组装消息通过子父进程通讯给钉钉主进程, 由主进程和服务器进行通讯完成该功能。后续通过折半查找法,定位到了钉钉的消息队列处理线程。所以这个时候取消malloc的断点开始向上回溯,停到疑似创建成员指针的位置后下断重新变动台下成员,经过验证这个call则为创建成员指针。注意劫持的dll是air2.dll,将原dll名称改为air2Org.dll即可。
2021安洵杯PWN WP详解
2021-12-29 16:41:08
做了2021安洵杯线上赛题目,总体来说题目有简单有难的,难易程度合适,这次就做了pwn,把四道pwn题思路总结一下,重点是没几个人做出来的最后一道pwnsky,赛后做了复现。
对于堆的恐惧来自堆复杂的管理机制,相较于栈来说复杂太多了,再加上使用GDB调试学习堆时,每次堆分配时,调试起来相当的麻烦,所以一直都是理论学习,堆不敢碰不敢尝试。今日小明同学终于排除了心中对堆的恐惧,在高铁上尝试了一下堆,熟悉了堆的分配机制。题目分析基本信息分析查看文件类型,32位,没有去掉符号。notepad_new大致通过注释解释了一下分析过程,后面不再进行详细的分析。
写一个android中so文件反混淆的系列文章,目前这是第三篇。根据其他人的分析可知,libDexHelper.so是指令抽取的实现,libdexjni.so是VMP的实现。在android so文件攻防实战-百度加固免费版libbaiduprotect.so反混淆中我们是交叉引用拿到加密后的字符串和它对应的解密函数的表然后frida主动调用得到的解密后的字符串,但是在这里这个方法就不太好用了。
近日,国家计算机病毒应急处理中心对名为“NOPEN”的木马工具进行了攻击场景复现和技术分析。该木马工具针对Unix/Linux平台,可实现对目标的远程控制。根据“影子经纪人”泄露的NSA内部文件,该木马工具为美国国家安全局开发的网络武器。“NOPEN”木马工具是一款功能强大的综合型木马工具,也是美国国家安全局接入技术行动处(TAO)对外攻击窃密所使用的主战网络武器之一。
在金融行业数字化转型的时代背景下,数据已成为金融业安全、高效、可控发展和管理的关键要素。
控制流劫持攻击是当前较为主流的攻击方式之一,包括ROP、JOP等等。
从代码视角来分析漏洞问题
VSole
网络安全专家